netty5/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java
Chris Vest 59275fba52
Netty Future no longer extends JDK Future (#11647)
Motivation:
It is important to avoid blocking method calls in an event loop thread, since that can stall the system.
Netty's Future interface was extending the JDK Future interface, which included a number of blocking methods of questionable use in Netty.
We wish to reduce the number of blocking methods on the Future API in order to discourage their use a little.
Further more, the Netty Future specification of the behaviour of the cancel() and isDone() methods are inconsistent with those of the JDK Future.
If Netty's Future stop extending the JDK Future interface, it will also no longer be bound by its specification.

Modification:
Make Netty's Future no longer extend the JDK Future interface.
Change the EvenExecutorGroup interface to no longer extend ScheduledExecutorService.
The EventExecutorGroup still extends Executor, because Executor does not dictate any return type of the `execute()` method — this is also useful in the DefaultFutureCompletionStage implementation.
The Netty ScheduledFuture interface has been removed since it provided no additional features that were actually used.
Numerous changes to use sites that previously relied on the JDK types.
Remove the `Future.cancel()` method that took a boolean argument — this argument was always ignored in our implementations, which was another spec deviation.
Various `invoke*` and `shutdown*` methods have been removed from the EvenExecutorGroup API since it no longer extends ScheduledExecutorService — these were either not used anywhere, or deprecated with better alternatives available.
Updates to cancellation javadocs.

Result:
Cleaner code, leaner API.
2021-09-08 09:06:28 +02:00

134 lines
5.1 KiB
Java

/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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:
*
* https://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 io.netty.util.concurrent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import static java.util.Objects.requireNonNull;
/**
* Abstract base class for {@link EventExecutor} implementations.
*/
public abstract class AbstractEventExecutor implements EventExecutor {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractEventExecutor.class);
static final long DEFAULT_SHUTDOWN_QUIET_PERIOD = 2;
static final long DEFAULT_SHUTDOWN_TIMEOUT = 15;
private final Future<?> successfulVoidFuture = DefaultPromise.newSuccessfulPromise(this, null).asFuture();
@Override
public <V> Future<V> newSucceededFuture(V result) {
if (result == null) {
@SuppressWarnings("unchecked")
Future<V> f = (Future<V>) successfulVoidFuture;
return f;
}
return EventExecutor.super.newSucceededFuture(result);
}
@Override
public final Future<Void> submit(Runnable task) {
var futureTask = newTaskFor(task, (Void) null);
execute(futureTask);
return futureTask;
}
@Override
public final <T> Future<T> submit(Runnable task, T result) {
var futureTask = newTaskFor(task, result);
execute(futureTask);
return futureTask;
}
@Override
public final <T> Future<T> submit(Callable<T> task) {
var futureTask = newTaskFor(task);
execute(futureTask);
return futureTask;
}
/**
* Decorate the given {@link Runnable} and its return value, as a {@link RunnableFuture}, such that the
* returned {@link RunnableFuture} completes with the given result at the end of executing its
* {@link RunnableFuture#run()} method.
* <p>
* The returned {@link RunnableFuture} is the task that will actually be run by a thread in this
* executor.
* <p>
* This method can be overridden by sub-classes to hook into the life cycle of the given task.
*
* @param runnable The task to be decorated.
* @param value The value that the returned future will complete with, assuming the given {@link Runnable} doesn't
* throw an exception.
* @param <T> The type of the result value.
* @return The decorated {@link Runnable} that is now also a {@link Future}.
*/
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return newRunnableFuture(newPromise(), runnable, value);
}
/**
* Decorate the given {@link Callable} and its return value, as a {@link RunnableFuture}, such that the
* returned {@link RunnableFuture} completes with the returned result from the {@link Callable} at the end of
* executing its {@link RunnableFuture#run()} method.
* <p>
* The returned {@link RunnableFuture} is the task that will actually be run by a thread in this
* executor.
* <p>
* This method can be overridden by sub-classes to hook into the life cycle of the given task.
*
* @param callable The task to be decorated.
* @param <T> The type of the result value.
* @return The decorated {@link Runnable} that is now also a {@link Future}.
*/
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return newRunnableFuture(newPromise(), callable);
}
/**
* Try to execute the given {@link Runnable} and just log if it throws a {@link Throwable}.
*/
static void safeExecute(Runnable task) {
try {
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
/**
* Returns a new {@link RunnableFuture} build on top of the given {@link Promise} and {@link Callable}.
*
* This can be used if you want to override {@link #newTaskFor(Callable)} and return a different
* {@link RunnableFuture}.
*/
private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Callable<V> task) {
return new RunnableFutureAdapter<>(promise, requireNonNull(task, "task"));
}
/**
* Returns a new {@link RunnableFuture} build on top of the given {@link Promise} and {@link Runnable} and
* {@code value}.
*/
private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Runnable task, V value) {
return new RunnableFutureAdapter<>(promise, Executors.callable(requireNonNull(task, "task"), value));
}
}