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.
This commit is contained in:
Chris Vest 2021-09-08 09:06:28 +02:00 committed by GitHub
parent 3cbb41a478
commit 59275fba52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 809 additions and 829 deletions

View File

@ -504,7 +504,7 @@ public abstract class WebSocketClientHandshaker {
}, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS); }, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS);
channel.closeFuture().addListener(ignore -> { channel.closeFuture().addListener(ignore -> {
forceCloseFuture.cancel(false); forceCloseFuture.cancel();
}); });
} }
}); });

View File

@ -117,7 +117,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
// Cancel the handshake timeout when handshake is finished. // Cancel the handshake timeout when handshake is finished.
localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel(false)); localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel());
} }
/** /**

View File

@ -125,7 +125,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
} }
}, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS); }, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS);
closeSent.asFuture().addListener(future -> timeoutTask.cancel(false)); closeSent.asFuture().addListener(future -> timeoutTask.cancel());
} }
/** /**

View File

@ -157,6 +157,6 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
// Cancel the handshake timeout when handshake is finished. // Cancel the handshake timeout when handshake is finished.
localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel(false)); localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel());
} }
} }

View File

@ -27,7 +27,6 @@ import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -900,7 +899,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
@Override @Override
public void operationComplete(Future<?> sentGoAwayFuture) { public void operationComplete(Future<?> sentGoAwayFuture) {
if (timeoutTask != null) { if (timeoutTask != null) {
timeoutTask.cancel(false); timeoutTask.cancel();
} }
doClose(); doClose();
} }

View File

@ -18,14 +18,15 @@ package io.netty.util.concurrent;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import static java.util.Objects.requireNonNull;
/** /**
* Abstract base class for {@link EventExecutor} implementations. * Abstract base class for {@link EventExecutor} implementations.
*/ */
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor { public abstract class AbstractEventExecutor implements EventExecutor {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractEventExecutor.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractEventExecutor.class);
static final long DEFAULT_SHUTDOWN_QUIET_PERIOD = 2; static final long DEFAULT_SHUTDOWN_QUIET_PERIOD = 2;
static final long DEFAULT_SHUTDOWN_TIMEOUT = 15; static final long DEFAULT_SHUTDOWN_TIMEOUT = 15;
@ -43,26 +44,60 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
} }
@Override @Override
public final Future<?> submit(Runnable task) { public final Future<Void> submit(Runnable task) {
return (Future<?>) super.submit(task); var futureTask = newTaskFor(task, (Void) null);
execute(futureTask);
return futureTask;
} }
@Override @Override
public final <T> Future<T> submit(Runnable task, T result) { public final <T> Future<T> submit(Runnable task, T result) {
return (Future<T>) super.submit(task, result); var futureTask = newTaskFor(task, result);
execute(futureTask);
return futureTask;
} }
@Override @Override
public final <T> Future<T> submit(Callable<T> task) { public final <T> Future<T> submit(Callable<T> task) {
return (Future<T>) super.submit(task); var futureTask = newTaskFor(task);
execute(futureTask);
return futureTask;
} }
@Override /**
* 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) { protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return newRunnableFuture(newPromise(), runnable, value); return newRunnableFuture(newPromise(), runnable, value);
} }
@Override /**
* 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) { protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return newRunnableFuture(newPromise(), callable); return newRunnableFuture(newPromise(), callable);
} }
@ -85,17 +120,14 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
* {@link RunnableFuture}. * {@link RunnableFuture}.
*/ */
private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Callable<V> task) { private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Callable<V> task) {
return new RunnableFutureAdapter<>(promise, 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 * Returns a new {@link RunnableFuture} build on top of the given {@link Promise} and {@link Runnable} and
* {@code value}. * {@code value}.
*
* This can be used if you want to override {@link #newTaskFor(Runnable, V)} and return a different
* {@link RunnableFuture}.
*/ */
private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Runnable task, V value) { private static <V> RunnableFuture<V> newRunnableFuture(Promise<V> promise, Runnable task, V value) {
return new RunnableFutureAdapter<>(promise, Executors.callable(task, value)); return new RunnableFutureAdapter<>(promise, Executors.callable(requireNonNull(task, "task"), value));
} }
} }

View File

@ -15,8 +15,6 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import static java.util.Objects.requireNonNull;
import io.netty.util.internal.DefaultPriorityQueue; import io.netty.util.internal.DefaultPriorityQueue;
import io.netty.util.internal.PriorityQueue; import io.netty.util.internal.PriorityQueue;
import io.netty.util.internal.PriorityQueueNode; import io.netty.util.internal.PriorityQueueNode;
@ -24,12 +22,13 @@ import io.netty.util.internal.PriorityQueueNode;
import java.util.Comparator; import java.util.Comparator;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.Executors.callable;
/** /**
* Abstract base class for {@link EventExecutor}s that want to support scheduling. * Abstract base class for {@link EventExecutor}s that want to support scheduling.
*/ */
@ -79,7 +78,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
/** /**
* Cancel all scheduled tasks. * Cancel all scheduled tasks.
* * <p>
* This method MUST be called only when {@link #inEventLoop()} is {@code true}. * This method MUST be called only when {@link #inEventLoop()} is {@code true}.
*/ */
protected final void cancelScheduledTasks() { protected final void cancelScheduledTasks() {
@ -93,7 +92,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
scheduledTaskQueue.toArray(EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES); scheduledTaskQueue.toArray(EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES);
for (RunnableScheduledFutureNode<?> task : scheduledTasks) { for (RunnableScheduledFutureNode<?> task : scheduledTasks) {
task.cancel(false); task.cancel();
} }
scheduledTaskQueue.clearIgnoringIndexes(); scheduledTaskQueue.clearIgnoringIndexes();
@ -107,9 +106,9 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
/** /**
* Return the {@link Runnable} which is ready to be executed with the given {@code nanoTime}. * Return the {@link Runnable} which is ready to be executed with the given {@code nanoTime}. You should use {@link
* You should use {@link #nanoTime()} to retrieve the correct {@code nanoTime}. * #nanoTime()} to retrieve the correct {@code nanoTime}.
* * <p>
* This method MUST be called only when {@link #inEventLoop()} is {@code true}. * This method MUST be called only when {@link #inEventLoop()} is {@code true}.
*/ */
protected final RunnableScheduledFuture<?> pollScheduledTask(long nanoTime) { protected final RunnableScheduledFuture<?> pollScheduledTask(long nanoTime) {
@ -130,7 +129,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
/** /**
* Return the nanoseconds when the next scheduled task is ready to be run or {@code -1} if no task is scheduled. * Return the nanoseconds when the next scheduled task is ready to be run or {@code -1} if no task is scheduled.
* * <p>
* This method MUST be called only when {@link #inEventLoop()} is {@code true}. * This method MUST be called only when {@link #inEventLoop()} is {@code true}.
*/ */
protected final long nextScheduledTaskNano() { protected final long nextScheduledTaskNano() {
@ -152,7 +151,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
/** /**
* Returns {@code true} if a scheduled task is ready for processing. * Returns {@code true} if a scheduled task is ready for processing.
* * <p>
* This method MUST be called only when {@link #inEventLoop()} is {@code true}. * This method MUST be called only when {@link #inEventLoop()} is {@code true}.
*/ */
protected final boolean hasScheduledTasks() { protected final boolean hasScheduledTasks() {
@ -163,19 +162,19 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> schedule(Runnable command, long delay, TimeUnit unit) {
requireNonNull(command, "command"); requireNonNull(command, "command");
requireNonNull(unit, "unit"); requireNonNull(unit, "unit");
if (delay < 0) { if (delay < 0) {
delay = 0; delay = 0;
} }
RunnableScheduledFuture<?> task = newScheduledTaskFor(Executors.callable(command), RunnableScheduledFuture<Void> task = newScheduledTaskFor(
deadlineNanos(unit.toNanos(delay)), 0); callable(command, null), deadlineNanos(unit.toNanos(delay)), 0);
return schedule(task); return schedule(task);
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
requireNonNull(callable, "callable"); requireNonNull(callable, "callable");
requireNonNull(unit, "unit"); requireNonNull(unit, "unit");
if (delay < 0) { if (delay < 0) {
@ -186,7 +185,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
requireNonNull(command, "command"); requireNonNull(command, "command");
requireNonNull(unit, "unit"); requireNonNull(unit, "unit");
if (initialDelay < 0) { if (initialDelay < 0) {
@ -198,13 +197,13 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
String.format("period: %d (expected: > 0)", period)); String.format("period: %d (expected: > 0)", period));
} }
RunnableScheduledFuture<?> task = newScheduledTaskFor(Executors.<Void>callable(command, null), RunnableScheduledFuture<Void> task = newScheduledTaskFor(
deadlineNanos(unit.toNanos(initialDelay)), unit.toNanos(period)); callable(command, null), deadlineNanos(unit.toNanos(initialDelay)), unit.toNanos(period));
return schedule(task); return schedule(task);
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { public Future<Void> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
requireNonNull(command, "command"); requireNonNull(command, "command");
requireNonNull(unit, "unit"); requireNonNull(unit, "unit");
if (initialDelay < 0) { if (initialDelay < 0) {
@ -216,15 +215,15 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
String.format("delay: %d (expected: > 0)", delay)); String.format("delay: %d (expected: > 0)", delay));
} }
RunnableScheduledFuture<?> task = newScheduledTaskFor(Executors.<Void>callable(command, null), RunnableScheduledFuture<Void> task = newScheduledTaskFor(
deadlineNanos(unit.toNanos(initialDelay)), -unit.toNanos(delay)); callable(command, null), deadlineNanos(unit.toNanos(initialDelay)), -unit.toNanos(delay));
return schedule(task); return schedule(task);
} }
/** /**
* Add the {@link RunnableScheduledFuture} for execution. * Add the {@link RunnableScheduledFuture} for execution.
*/ */
protected final <V> ScheduledFuture<V> schedule(final RunnableScheduledFuture<V> task) { protected final <V> Future<V> schedule(final RunnableScheduledFuture<V> task) {
if (inEventLoop()) { if (inEventLoop()) {
add0(task); add0(task);
} else { } else {
@ -253,9 +252,9 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
/** /**
* Returns a new {@link RunnableFuture} build on top of the given {@link Promise} and {@link Callable}. * Returns a new {@link RunnableFuture} build on top of the given {@link Promise} and {@link Callable}.
* * <p>
* This can be used if you want to override {@link #newTaskFor(Callable)} and return a different * This can be used if you want to override {@link #newScheduledTaskFor(Callable, long, long)} and return a
* {@link RunnableFuture}. * different {@link RunnableFuture}.
*/ */
protected static <V> RunnableScheduledFuture<V> newRunnableScheduledFuture( protected static <V> RunnableScheduledFuture<V> newRunnableScheduledFuture(
AbstractScheduledEventExecutor executor, Promise<V> promise, Callable<V> task, AbstractScheduledEventExecutor executor, Promise<V> promise, Callable<V> task,
@ -271,7 +270,8 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
return newRunnableScheduledFuture(this, newPromise(), callable, deadlineNanos, period); return newRunnableScheduledFuture(this, newPromise(), callable, deadlineNanos, period);
} }
interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> { } interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> {
}
private static final class DefaultRunnableScheduledFutureNode<V> implements RunnableScheduledFutureNode<V> { private static final class DefaultRunnableScheduledFutureNode<V> implements RunnableScheduledFutureNode<V> {
private final RunnableScheduledFuture<V> future; private final RunnableScheduledFuture<V> future;
@ -308,8 +308,8 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public <C> RunnableScheduledFuture<V> addListener(C context, public <C> RunnableScheduledFuture<V> addListener(
FutureContextListener<? super C, ? super V> listener) { C context, FutureContextListener<? super C, ? super V> listener) {
future.addListener(context, listener); future.addListener(context, listener);
return this; return this;
} }
@ -335,8 +335,8 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel() {
return future.cancel(mayInterruptIfRunning); return future.cancel();
} }
@Override @Override
@ -360,12 +360,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public long getDelay(TimeUnit unit) { public int compareTo(RunnableScheduledFuture<?> o) {
return future.getDelay(unit);
}
@Override
public int compareTo(Delayed o) {
return future.compareTo(o); return future.compareTo(o);
} }
@ -393,11 +388,6 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
return this; return this;
} }
@Override
public boolean cancel() {
return cancel(false);
}
@Override @Override
public boolean isSuccess() { public boolean isSuccess() {
return future.isSuccess(); return future.isSuccess();

View File

@ -25,9 +25,10 @@ import java.util.concurrent.CancellationException;
*/ */
interface AsynchronousResult<V> { interface AsynchronousResult<V> {
/** /**
* Cancel this asynchronous operation, unless it has already been completed. * Cancel this asynchronous operation, unless it has already been completed
* or is not {@linkplain #isCancellable() cancellable}.
* <p> * <p>
* A cancelled operation is considered to be {@linkplain #isFailed() failed}. * A cancelled operation is considered to be {@linkplain #isDone() done} and {@linkplain #isFailed() failed}.
* <p> * <p>
* If the cancellation was successful, the result of this operation will be that it has failed with a * If the cancellation was successful, the result of this operation will be that it has failed with a
* {@link CancellationException}. * {@link CancellationException}.
@ -66,7 +67,11 @@ interface AsynchronousResult<V> {
boolean isDone(); boolean isDone();
/** /**
* returns {@code true} if and only if the operation can be cancelled via {@link #cancel()}. * Returns {@code true} if and only if the operation can be cancelled via {@link #cancel()}.
* Note that this is inherently racy, as the operation could be made
* {@linkplain Promise#setUncancellable() uncancellable} at any time.
*
* @return {@code true} if this operation can be cancelled.
*/ */
boolean isCancellable(); boolean isCancellable();

View File

@ -17,8 +17,11 @@ package io.netty.util.concurrent;
import java.util.concurrent.CompletionStage; import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -33,7 +36,7 @@ import static java.util.Objects.requireNonNull;
* *
* @param <V> the value type. * @param <V> the value type.
*/ */
final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V> { final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>, java.util.concurrent.Future<V> {
private enum Marker { private enum Marker {
EMPTY, EMPTY,
ERROR ERROR
@ -50,6 +53,31 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
this.future = future; this.future = future;
} }
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel();
}
@Override
public boolean isCancelled() {
return future.isCancelled();
}
@Override
public boolean isDone() {
return future.isDone();
}
@Override
public V get() throws InterruptedException, ExecutionException {
return future.get();
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return future.get(timeout, unit);
}
@Override @Override
public Future<V> future() { public Future<V> future() {
return future; return future;

View File

@ -384,14 +384,6 @@ public class DefaultPromise<V> implements Promise<V>, Future<V> {
@Override @Override
public boolean cancel() { public boolean cancel() {
return cancel(false);
}
/**
* @param mayInterruptIfRunning this value has no effect in this implementation.
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (RESULT_UPDATER.compareAndSet(this, null, CANCELLATION_CAUSE_HOLDER)) { if (RESULT_UPDATER.compareAndSet(this, null, CANCELLATION_CAUSE_HOLDER)) {
if (checkNotifyWaiters()) { if (checkNotifyWaiters()) {
notifyListeners(); notifyListeners();
@ -668,6 +660,15 @@ public class DefaultPromise<V> implements Promise<V>, Future<V> {
@Override @Override
public FutureCompletionStage<V> asStage() { public FutureCompletionStage<V> asStage() {
return getFutureStageAdaptor();
}
@Override
public java.util.concurrent.Future<V> asJdkFuture() {
return getFutureStageAdaptor();
}
private DefaultFutureCompletionStage<V> getFutureStageAdaptor() {
DefaultFutureCompletionStage<V> stageAdapter = stage; DefaultFutureCompletionStage<V> stageAdapter = stage;
if (stageAdapter == null) { if (stageAdapter == null) {
stage = stageAdapter = new DefaultFutureCompletionStage<>(this); stage = stageAdapter = new DefaultFutureCompletionStage<>(this);

View File

@ -15,15 +15,10 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_QUIET_PERIOD; import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_QUIET_PERIOD;
import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_TIMEOUT; import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_TIMEOUT;
@ -34,61 +29,88 @@ import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_TI
* life-cycle and allows shutting them down in a global fashion. * life-cycle and allows shutting them down in a global fashion.
* *
*/ */
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor> { public interface EventExecutorGroup extends Iterable<EventExecutor>, Executor {
/** /**
* Returns {@code true} if and only if all {@link EventExecutor}s managed by this {@link EventExecutorGroup} * Returns {@code true} if and only if all {@link EventExecutor}s managed by this {@link EventExecutorGroup}
* are being {@linkplain #shutdownGracefully() shut down gracefully} or was {@linkplain #isShutdown() shut down}. * are being {@linkplain #shutdownGracefully() shut down gracefully} or was {@linkplain #isShutdown() shut down}.
* <p>
* An executor group that "is shutting down" can still accept new tasks for a little while (the grace period),
* but will eventually start rejecting new tasks.
* At that point, the executor group will be {@linkplain #isShutdown() shut down}.
*
* @return {@code true} if all executors in this group have at least started shutting down, otherwise {@code false}.
*/ */
boolean isShuttingDown(); boolean isShuttingDown();
/**
* Returns {@code true} if all {@link EventExecutor}s managed by this {@link EventExecutorGroup} have been
* {@linkplain #shutdownGracefully() shut down gracefully} and moved past the grace period so that they are no
* longer accepting any new tasks.
* <p>
* An executor group that "is shut down" might still be executing tasks that it has queued up, but it will no
* longer be accepting any new tasks.
* Once all running and queued tasks have completed, the executor group will be
* {@linkplain #isTerminated() terminated}.
*
* @return {@code true} if all executors in this group have shut down and are no longer accepting any new tasks.
*/
boolean isShutdown();
/**
* Returns {@code true} if all {@link EventExecutor}s managed by this {@link EventExecutorGroup} are
* {@linkplain #isShutdown() shut down}, and all of their tasks have completed.
*
* @return {@code true} if all executors in this group have terminated.
*/
default boolean isTerminated() {
return terminationFuture().isDone();
}
/**
* Wait for this {@link EventExecutorGroup} to {@linkplain #isTerminated() terminate}, up to the given timeout.
*
* @param timeout The non-negative maximum amount of time to wait for the executor group to terminate.
* @param unit The non-null time unit of the timeout.
* @return {@code true} if the executor group terminated within the specific timeout.
* @throws InterruptedException If this thread was {@linkplain Thread#interrupt() interrupted} while waiting for
* executor group to terminate.
*/
default boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return terminationFuture().await(timeout, unit);
}
/** /**
* Shortcut method for {@link #shutdownGracefully(long, long, TimeUnit)} with sensible default values. * Shortcut method for {@link #shutdownGracefully(long, long, TimeUnit)} with sensible default values.
* *
* @return the {@link #terminationFuture()} * @return the {@link #terminationFuture()}
*/ */
default Future<?> shutdownGracefully() { default Future<Void> shutdownGracefully() {
return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS); return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
} }
/** /**
* Signals this executor that the caller wants the executor to be shut down. Once this method is called, * Signals this executor that the caller wants the executor to be shut down. Once this method is called,
* {@link #isShuttingDown()} starts to return {@code true}, and the executor prepares to shut itself down. * {@link #isShuttingDown()} starts to return {@code true}, and the executor prepares to shut itself down.
* Unlike {@link #shutdown()}, graceful shutdown ensures that no tasks are submitted for <i>'the quiet period'</i> * This method ensures that no tasks are submitted for <i>'the quiet period'</i> (usually a couple seconds) before
* (usually a couple seconds) before it shuts itself down. If a task is submitted during the quiet period, * it shuts itself down. If a task is submitted during the quiet period, it is guaranteed to be accepted and the
* it is guaranteed to be accepted and the quiet period will start over. * quiet period will start over.
* *
* @param quietPeriod the quiet period as described in the documentation * @param quietPeriod the quiet period as described in the documentation
* @param timeout the maximum amount of time to wait until the executor is {@linkplain #shutdown()} * @param timeout the maximum amount of time to wait until the executor is
* regardless if a task was submitted during the quiet period * {@linkplain #isShuttingDown() shutting down} regardless if a task was submitted during the quiet period.
* @param unit the unit of {@code quietPeriod} and {@code timeout} * @param unit the unit of {@code quietPeriod} and {@code timeout}
* *
* @return the {@link #terminationFuture()} * @return the {@link #terminationFuture()}
*/ */
Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit); Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit);
/** /**
* Returns the {@link Future} which is notified when all {@link EventExecutor}s managed by this * Returns the {@link Future} which is notified when all {@link EventExecutor}s managed by this
* {@link EventExecutorGroup} have been terminated. * {@link EventExecutorGroup} have been terminated.
*
* @return The {@link Future} representing the termination of this {@link EventExecutorGroup}.
*/ */
Future<?> terminationFuture(); Future<Void> terminationFuture();
/**
* @deprecated {@link #shutdownGracefully(long, long, TimeUnit)} or {@link #shutdownGracefully()} instead.
*/
@Override
@Deprecated
void shutdown();
/**
* @deprecated {@link #shutdownGracefully(long, long, TimeUnit)} or {@link #shutdownGracefully()} instead.
*/
@Override
@Deprecated
default List<Runnable> shutdownNow() {
shutdown();
return Collections.emptyList();
}
/** /**
* Returns one of the {@link EventExecutor}s managed by this {@link EventExecutorGroup}. * Returns one of the {@link EventExecutor}s managed by this {@link EventExecutorGroup}.
@ -98,66 +120,116 @@ public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<E
@Override @Override
Iterator<EventExecutor> iterator(); Iterator<EventExecutor> iterator();
@Override /**
default Future<?> submit(Runnable task) { * Submit the given task for execution in the next available {@link EventExecutor} in this group,
* and return a future that produces a {@code null} result when the task completes.
*
* @param task The task that should be executed in this {@link EventExecutorGroup}.
* @return A future that represents the completion of the submitted task.
*/
default Future<Void> submit(Runnable task) {
return next().submit(task); return next().submit(task);
} }
@Override /**
* Submit the given task for execution in the next available {@link EventExecutor} in this group,
* and return a future that produces the given result when the task completes.
*
* @param task The task that should be executed in this {@link EventExecutorGroup}.
* @param result The value that the returned future will complete with, if the task completes successfully.
* @param <T> The type of the future result.
* @return A future that represents the completion of the submitted task.
*/
default <T> Future<T> submit(Runnable task, T result) { default <T> Future<T> submit(Runnable task, T result) {
return next().submit(task, result); return next().submit(task, result);
} }
@Override /**
* Submit the given task for execution in the next available {@link EventExecutor} in this group,
* and return a future that will return the result of the callable when the task completes.
*
* @param task The task that should be executed in this {@link EventExecutorGroup}.
* @param <T> The type of the future result.
* @return A future that represents the completion of the submitted task.
*/
default <T> Future<T> submit(Callable<T> task) { default <T> Future<T> submit(Callable<T> task) {
return next().submit(task); return next().submit(task);
} }
@Override /**
default ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { * Schedule the given task for execution after the given delay, in the next available {@link EventExecutor}
return next().schedule(command, delay, unit); * in this group, and return a future that produces a {@code null} result when the task completes.
*
* @param task The task that should be executed in this {@link EventExecutorGroup} after the given delay.
* @param delay A positive time delay, in the given time unit.
* @param unit The non-null time unit for the delay.
* @return A future that represents the completion of the scheduled task.
*/
default Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return next().schedule(task, delay, unit);
}
/**
* Schedule the given task for execution after the given delay, in the next available {@link EventExecutor}
* in this group, and return a future that will return the result of the callable when the task completes.
*
* @param task The task that should be executed in this {@link EventExecutorGroup} after the given delay.
* @param delay A positive time delay, in the given time unit.
* @param unit The non-null time unit for the delay.
* @param <V> The type of the future result.
* @return A future that represents the completion of the scheduled task.
*/
default <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return next().schedule(task, delay, unit);
}
/**
* Schedule the given task for periodic execution in the next available {@link EventExecutor}.
* The first execution will occur after the given initial delay, and the following repeated executions will occur
* with the given period of time between each execution is started.
* If the task takes longer to complete than the requested period, then the following executions will be delayed,
* rather than allowing multiple instances of the task to run concurrently.
* <p>
* The task will be executed repeatedly until it either fails with an exception, or its future is
* {@linkplain Future#cancel() cancelled}. The future thus will never complete successfully.
*
* @param task The task that should be scheduled to execute at a fixed rate in this {@link EventExecutorGroup}.
* @param initialDelay The positive initial delay for the first task execution, in terms of the given time unit.
* @param period The positive period for the execution frequency to use after the first execution has started,
* in terms of the given time unit.
* @param unit The non-null time unit for the delay and period.
* @return A future that represents the recurring task, and which can be cancelled to stop future executions.
*/
default Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return next().scheduleAtFixedRate(task, initialDelay, period, unit);
}
/**
* Schedule the given task for periodic execution in the next available {@link EventExecutor}.
* The first execution will occur after the given initial delay, and the following repeated executions will occur
* with the given subsequent delay between one task completing and the next task starting.
* The delay from the completion of one task, to the start of the next, stays unchanged regardless of how long a
* task takes to complete.
* <p>
* This is in contrast to {@link #scheduleAtFixedRate(Runnable, long, long, TimeUnit)} which varies the delays
* between the tasks in order to hit a given frequency.
* <p>
* The task will be executed repeatedly until it either fails with an exception, or its future is
* {@linkplain Future#cancel() cancelled}. The future thus will never complete successfully.
*
* @param task The task that should be scheduled to execute with fixed delays in this {@link EventExecutorGroup}.
* @param initialDelay The positive initial delay for the first task execution, in terms of the given time unit.
* @param delay The positive subsequent delay between task, to use after the first execution has completed,
* in terms of the given time unit.
* @param unit The non-null time unit for the delays.
* @return A future that represents the recurring task, and which can be cancelled to stop future executions.
*/
default Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return next().scheduleWithFixedDelay(task, initialDelay, delay, unit);
} }
@Override @Override
default <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { default void execute(Runnable task) {
return next().schedule(callable, delay, unit); next().execute(task);
}
@Override
default ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return next().scheduleAtFixedRate(command, initialDelay, period, unit);
}
@Override
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return next().scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
@Override
default <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return next().invokeAll(tasks);
}
@Override
default <T> List<java.util.concurrent.Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
return next().invokeAll(tasks, timeout, unit);
}
@Override
default <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return next().invokeAny(tasks);
}
@Override
default <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return next().invokeAny(tasks, timeout, unit);
}
@Override
default void execute(Runnable command) {
next().execute(command);
} }
} }

View File

@ -150,8 +150,7 @@ import java.util.function.Function;
* } * }
* </pre> * </pre>
*/ */
@SuppressWarnings("ClassNameSameAsAncestorName") public interface Future<V> extends AsynchronousResult<V> {
public interface Future<V> extends java.util.concurrent.Future<V>, AsynchronousResult<V> {
/** /**
* Adds the specified listener to this future. The specified listener is notified when this future is {@linkplain * Adds the specified listener to this future. The specified listener is notified when this future is {@linkplain
* #isDone() done}. If this future is already completed, the specified listener is notified immediately. * #isDone() done}. If this future is already completed, the specified listener is notified immediately.
@ -236,14 +235,15 @@ public interface Future<V> extends java.util.concurrent.Future<V>, AsynchronousR
boolean awaitUninterruptibly(long timeoutMillis); boolean awaitUninterruptibly(long timeoutMillis);
/** /**
* {@inheritDoc} * Get the result of this future, if it has completed.
* <p> * If the future has failed, then an {@link ExecutionException} will be thrown instead.
* If the cancellation was successful it will fail the future with a {@link CancellationException}. * If the future has not yet completed, then this method will block until it completes.
*
* @return The result of the task execution, if it completed successfully.
* @throws InterruptedException If the call was blocked, waiting for the future to complete, and the thread was
* {@linkplain Thread#interrupt() interrupted}.
* @throws ExecutionException If the task failed, either by throwing an exception or through cancellation.
*/ */
@Override
boolean cancel(boolean mayInterruptIfRunning);
@Override
default V get() throws InterruptedException, ExecutionException { default V get() throws InterruptedException, ExecutionException {
await(); await();
@ -257,7 +257,24 @@ public interface Future<V> extends java.util.concurrent.Future<V>, AsynchronousR
throw new ExecutionException(cause); throw new ExecutionException(cause);
} }
@Override /**
* Get the result of this future, if it has completed.
* If the future has failed, then an {@link ExecutionException} will be thrown instead.
* If the future has not yet completed, then this method will block, waiting up to the given timeout for the future
* to complete.
* If the future does not complete within the specified timeout, then a {@link TimeoutException} will be thrown.
* If the timeout is zero, then this method will not block, and instead either get the result or failure of the
* future if completed, or immediately throw a {@link TimeoutException} if not yet completed.
*
* @param timeout The non-negative maximum amount of time, in terms of the given time unit, to wait for the
* completion of the future.
* @param unit The time unit for the timeout.
* @return The value of the successfully completed future.
* @throws InterruptedException If this call was blocking and this thread got
* {@linkplain Thread#interrupt() interrupted}.
* @throws ExecutionException If the task failed, either by throwing an exception, or through cancellation.
* @throws TimeoutException If the future did not complete within the specified timeout.
*/
default V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { default V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (await(timeout, unit)) { if (await(timeout, unit)) {
Throwable cause = cause(); Throwable cause = cause();
@ -280,6 +297,13 @@ public interface Future<V> extends java.util.concurrent.Future<V>, AsynchronousR
return new DefaultFutureCompletionStage<>(this); return new DefaultFutureCompletionStage<>(this);
} }
/**
* Returns a {@link java.util.concurrent.Future JDK Future that reflects the state of this {@link Future}.
*/
default java.util.concurrent.Future<V> asJdkFuture() {
return new DefaultFutureCompletionStage<>(this);
}
/** /**
* Creates a <strong>new</strong> {@link Future} that will complete with the result of this {@link Future} mapped * Creates a <strong>new</strong> {@link Future} that will complete with the result of this {@link Future} mapped
* through the given mapper function. * through the given mapper function.

View File

@ -136,7 +136,7 @@ final class Futures {
@Override @Override
public void operationComplete(Future<Object> context, Future<?> future) throws Exception { public void operationComplete(Future<Object> context, Future<?> future) throws Exception {
if (future.isCancelled()) { if (future.isCancelled()) {
context.cancel(false); context.cancel();
} }
} }
} }

View File

@ -67,7 +67,7 @@ public final class GlobalEventExecutor extends AbstractScheduledEventExecutor im
private final AtomicBoolean started = new AtomicBoolean(); private final AtomicBoolean started = new AtomicBoolean();
volatile Thread thread; volatile Thread thread;
private final Future<?> terminationFuture = DefaultPromise.newFailedPromise( private final Future<Void> terminationFuture = DefaultPromise.<Void>newFailedPromise(
this, new UnsupportedOperationException()).asFuture(); this, new UnsupportedOperationException()).asFuture();
private GlobalEventExecutor() { private GlobalEventExecutor() {
@ -150,21 +150,15 @@ public final class GlobalEventExecutor extends AbstractScheduledEventExecutor im
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return terminationFuture(); return terminationFuture();
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return terminationFuture; return terminationFuture;
} }
@Override
@Deprecated
public void shutdown() {
throw new UnsupportedOperationException();
}
@Override @Override
public boolean isShuttingDown() { public boolean isShuttingDown() {
return false; return false;

View File

@ -15,8 +15,6 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import static java.util.Objects.requireNonNull;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -25,6 +23,8 @@ import java.util.Queue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
/** /**
* Executes {@link Runnable} objects in the caller's thread. If the {@link #execute(Runnable)} is reentrant it will be * Executes {@link Runnable} objects in the caller's thread. If the {@link #execute(Runnable)} is reentrant it will be
* queued until the original {@link Runnable} finishes execution. * queued until the original {@link Runnable} finishes execution.
@ -54,7 +54,7 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
} }
}; };
private final Future<?> terminationFuture = DefaultPromise.newFailedPromise( private final Future<Void> terminationFuture = DefaultPromise.<Void>newFailedPromise(
GlobalEventExecutor.INSTANCE, new UnsupportedOperationException()).asFuture(); GlobalEventExecutor.INSTANCE, new UnsupportedOperationException()).asFuture();
private ImmediateEventExecutor() { } private ImmediateEventExecutor() { }
@ -65,19 +65,15 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return terminationFuture(); return terminationFuture();
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return terminationFuture; return terminationFuture;
} }
@Override
@Deprecated
public void shutdown() { }
@Override @Override
public boolean isShuttingDown() { public boolean isShuttingDown() {
return false; return false;
@ -99,14 +95,14 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
requireNonNull(command, "command"); requireNonNull(task, "command");
if (!RUNNING.get()) { if (!RUNNING.get()) {
RUNNING.set(true); RUNNING.set(true);
try { try {
command.run(); task.run();
} catch (Throwable cause) { } catch (Throwable cause) {
logger.info("Throwable caught while executing Runnable {}", command, cause); logger.info("Throwable caught while executing Runnable {}", task, cause);
} finally { } finally {
Queue<Runnable> delayedRunnables = DELAYED_RUNNABLES.get(); Queue<Runnable> delayedRunnables = DELAYED_RUNNABLES.get();
Runnable runnable; Runnable runnable;
@ -120,7 +116,7 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
RUNNING.set(false); RUNNING.set(false);
} }
} else { } else {
DELAYED_RUNNABLES.get().add(command); DELAYED_RUNNABLES.get().add(task);
} }
} }
@ -130,23 +126,23 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, public Future<Void> schedule(Runnable task, long delay,
TimeUnit unit) { TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { public Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -38,7 +38,7 @@ public class MultithreadEventExecutorGroup implements EventExecutorGroup {
private final EventExecutor[] children; private final EventExecutor[] children;
private final List<EventExecutor> readonlyChildren; private final List<EventExecutor> readonlyChildren;
private final AtomicInteger terminatedChildren = new AtomicInteger(); private final AtomicInteger terminatedChildren = new AtomicInteger();
private final Promise<?> terminationFuture = GlobalEventExecutor.INSTANCE.newPromise(); private final Promise<Void> terminationFuture = GlobalEventExecutor.INSTANCE.newPromise();
private final boolean powerOfTwo; private final boolean powerOfTwo;
/** /**
@ -223,7 +223,7 @@ public class MultithreadEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public final Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public final Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
for (EventExecutor l: children) { for (EventExecutor l: children) {
l.shutdownGracefully(quietPeriod, timeout, unit); l.shutdownGracefully(quietPeriod, timeout, unit);
} }
@ -231,18 +231,10 @@ public class MultithreadEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public final Future<?> terminationFuture() { public final Future<Void> terminationFuture() {
return terminationFuture.asFuture(); return terminationFuture.asFuture();
} }
@Override
@Deprecated
public final void shutdown() {
for (EventExecutor l: children) {
l.shutdown();
}
}
@Override @Override
public final boolean isShuttingDown() { public final boolean isShuttingDown() {
for (EventExecutor l: children) { for (EventExecutor l: children) {

View File

@ -15,23 +15,19 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.util.Objects.requireNonNull;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.util.Objects.requireNonNull;
/** /**
* {@link EventExecutorGroup} which will preserve {@link Runnable} execution order but makes no guarantees about what * {@link EventExecutorGroup} which will preserve {@link Runnable} execution order but makes no guarantees about what
* {@link EventExecutor} (and therefore {@link Thread}) will be used to execute the {@link Runnable}s. * {@link EventExecutor} (and therefore {@link Thread}) will be used to execute the {@link Runnable}s.
@ -83,32 +79,20 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public Future<?> shutdownGracefully() { public Future<Void> shutdownGracefully() {
return group.shutdownGracefully(); return group.shutdownGracefully();
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return group.shutdownGracefully(quietPeriod, timeout, unit); return group.shutdownGracefully(quietPeriod, timeout, unit);
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return group.terminationFuture(); return group.terminationFuture();
} }
@SuppressWarnings("deprecation")
@Override
public void shutdown() {
group.shutdown();
}
@SuppressWarnings("deprecation")
@Override
public List<Runnable> shutdownNow() {
return group.shutdownNow();
}
@Override @Override
public EventExecutor next() { public EventExecutor next() {
return newExecutor(group.next()); return newExecutor(group.next());
@ -136,7 +120,7 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public Future<?> submit(Runnable task) { public Future<Void> submit(Runnable task) {
return group.submit(task); return group.submit(task);
} }
@ -151,23 +135,23 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return group.schedule(command, delay, unit); return group.schedule(task, delay, unit);
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return group.schedule(callable, delay, unit); return group.schedule(task, delay, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return group.scheduleAtFixedRate(command, initialDelay, period, unit); return group.scheduleAtFixedRate(task, initialDelay, period, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { public Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return group.scheduleWithFixedDelay(command, initialDelay, delay, unit); return group.scheduleWithFixedDelay(task, initialDelay, delay, unit);
} }
@Override @Override
@ -186,31 +170,8 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public <T> List<java.util.concurrent.Future<T>> invokeAll( public void execute(Runnable task) {
Collection<? extends Callable<T>> tasks) throws InterruptedException { group.execute(task);
return group.invokeAll(tasks);
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
return group.invokeAll(tasks, timeout, unit);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return group.invokeAny(tasks);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return group.invokeAny(tasks, timeout, unit);
}
@Override
public void execute(Runnable command) {
group.execute(command);
} }
private static final class NonStickyOrderedEventExecutor extends AbstractEventExecutor private static final class NonStickyOrderedEventExecutor extends AbstractEventExecutor
@ -294,20 +255,15 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return executor.shutdownGracefully(quietPeriod, timeout, unit); return executor.shutdownGracefully(quietPeriod, timeout, unit);
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return executor.terminationFuture(); return executor.terminationFuture();
} }
@Override
public void shutdown() {
executor.shutdown();
}
@Override @Override
public boolean isShutdown() { public boolean isShutdown() {
return executor.isShutdown(); return executor.isShutdown();
@ -324,8 +280,8 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
if (!tasks.offer(command)) { if (!tasks.offer(task)) {
throw new RejectedExecutionException(); throw new RejectedExecutionException();
} }
if (state.compareAndSet(NONE, SUBMITTED)) { if (state.compareAndSet(NONE, SUBMITTED)) {
@ -336,25 +292,25 @@ public final class NonStickyEventExecutorGroup implements EventExecutorGroup {
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, public Future<Void> schedule(Runnable task, long delay,
TimeUnit unit) { TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate( public Future<Void> scheduleAtFixedRate(
Runnable command, long initialDelay, long period, TimeUnit unit) { Runnable task, long initialDelay, long period, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay( public Future<Void> scheduleWithFixedDelay(
Runnable command, long initialDelay, long delay, TimeUnit unit) { Runnable task, long initialDelay, long delay, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -121,6 +121,11 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
return super.setUncancellable(); return super.setUncancellable();
} }
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return cancel();
}
@Override @Override
protected StringBuilder toStringBuilder() { protected StringBuilder toStringBuilder() {
StringBuilder buf = super.toStringBuilder(); StringBuilder buf = super.toStringBuilder();

View File

@ -18,8 +18,7 @@ package io.netty.util.concurrent;
/** /**
* A combination of {@link java.util.concurrent.RunnableFuture} and {@link Future}. * A combination of {@link java.util.concurrent.RunnableFuture} and {@link Future}.
*/ */
@SuppressWarnings("ClassNameSameAsAncestorName") public interface RunnableFuture<V> extends Runnable, Future<V> {
public interface RunnableFuture<V> extends java.util.concurrent.RunnableFuture<V>, Future<V> {
@Override @Override
RunnableFuture<V> addListener(FutureListener<? super V> listener); RunnableFuture<V> addListener(FutureListener<? super V> listener);

View File

@ -15,8 +15,6 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import static java.util.Objects.requireNonNull;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -24,6 +22,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import static java.util.Objects.requireNonNull;
final class RunnableFutureAdapter<V> implements RunnableFuture<V> { final class RunnableFutureAdapter<V> implements RunnableFuture<V> {
private final Promise<V> promise; private final Promise<V> promise;
@ -134,14 +134,9 @@ final class RunnableFutureAdapter<V> implements RunnableFuture<V> {
} }
} }
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel(mayInterruptIfRunning);
}
@Override @Override
public boolean cancel() { public boolean cancel() {
return cancel(false); return future.cancel();
} }
@Override @Override

View File

@ -16,12 +16,17 @@
package io.netty.util.concurrent; package io.netty.util.concurrent;
/** /**
* A combination of {@link java.util.concurrent.RunnableScheduledFuture}, {@link RunnableFuture} and * A combination of {@link RunnableFuture} and {@link Comparable} (sorting by their next deadline),
* {@link ScheduledFuture}. * with additional methods for scheduling, periodicity, and delay.
*/ */
@SuppressWarnings("ClassNameSameAsAncestorName") public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, Comparable<RunnableScheduledFuture<?>> {
public interface RunnableScheduledFuture<V> extends /**
java.util.concurrent.RunnableScheduledFuture<V>, RunnableFuture<V>, ScheduledFuture<V> { * Return {@code true} if the task is periodic, which means it may be executed multiple times, as opposed to a
* delayed task or a normal task, that only execute once.
*
* @return {@code true} if this task is periodic, otherwise {@code false}.
*/
boolean isPeriodic();
/** /**
* Returns the deadline in nanos when the {@link #run()} method should be called again. * Returns the deadline in nanos when the {@link #run()} method should be called again.

View File

@ -16,19 +16,17 @@
package io.netty.util.concurrent; package io.netty.util.concurrent;
import static java.util.Objects.requireNonNull;
import io.netty.util.internal.DefaultPriorityQueue; import io.netty.util.internal.DefaultPriorityQueue;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden") import static java.util.Objects.requireNonNull;
final class RunnableScheduledFutureAdapter<V> implements AbstractScheduledEventExecutor.RunnableScheduledFutureNode<V> { final class RunnableScheduledFutureAdapter<V> implements AbstractScheduledEventExecutor.RunnableScheduledFutureNode<V> {
private static final AtomicLong NEXT_TASK_ID = new AtomicLong(); private static final AtomicLong NEXT_TASK_ID = new AtomicLong();
@ -75,12 +73,7 @@ final class RunnableScheduledFutureAdapter<V> implements AbstractScheduledEventE
} }
@Override @Override
public long getDelay(TimeUnit unit) { public int compareTo(RunnableScheduledFuture<?> o) {
return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this == o) { if (this == o) {
return 0; return 0;
} }
@ -100,6 +93,23 @@ final class RunnableScheduledFutureAdapter<V> implements AbstractScheduledEventE
} }
} }
@Override
public int hashCode() {
return Long.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RunnableScheduledFutureAdapter) {
RunnableScheduledFutureAdapter<?> adaptor = (RunnableScheduledFutureAdapter<?>) obj;
return id == adaptor.id;
}
return false;
}
@Override @Override
public void run() { public void run() {
try { try {
@ -130,23 +140,15 @@ final class RunnableScheduledFutureAdapter<V> implements AbstractScheduledEventE
} }
} }
/**
* @param mayInterruptIfRunning this value has no effect in this implementation.
*/
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel() {
boolean canceled = future.cancel(mayInterruptIfRunning); boolean canceled = future.cancel();
if (canceled) { if (canceled) {
executor.removeScheduled(this); executor.removeScheduled(this);
} }
return canceled; return canceled;
} }
@Override
public boolean cancel() {
return cancel(false);
}
@Override @Override
public boolean isSuccess() { public boolean isSuccess() {
return promise.isSuccess(); return promise.isSuccess();

View File

@ -1,23 +0,0 @@
/*
* 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;
/**
* The result of an scheduled asynchronous operation.
*/
@SuppressWarnings("ClassNameSameAsAncestorName")
public interface ScheduledFuture<V> extends Future<V>, java.util.concurrent.ScheduledFuture<V> {
}

View File

@ -22,21 +22,17 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.Thread.State; import java.lang.Thread.State;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@ -95,7 +91,7 @@ public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor im
private volatile long gracefulShutdownTimeout; private volatile long gracefulShutdownTimeout;
private long gracefulShutdownStartTime; private long gracefulShutdownStartTime;
private final Promise<?> terminationFuture = new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE); private final Promise<Void> terminationFuture = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
/** /**
* Create a new instance * Create a new instance
@ -500,7 +496,7 @@ public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor im
} }
@Override @Override
public final Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public final Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
if (quietPeriod < 0) { if (quietPeriod < 0) {
throw new IllegalArgumentException("quietPeriod: " + quietPeriod + " (expected >= 0)"); throw new IllegalArgumentException("quietPeriod: " + quietPeriod + " (expected >= 0)");
} }
@ -538,7 +534,6 @@ public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor im
} }
} }
if (STATE_UPDATER.compareAndSet(this, oldState, newState)) { if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
//System.err.println(oldState + " " + newState + " " + this);
break; break;
} }
} }
@ -560,58 +555,10 @@ public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor im
} }
@Override @Override
public final Future<?> terminationFuture() { public final Future<Void> terminationFuture() {
return terminationFuture.asFuture(); return terminationFuture.asFuture();
} }
@Override
@Deprecated
public final void shutdown() {
if (isShutdown()) {
return;
}
boolean inEventLoop = inEventLoop();
boolean wakeup;
int oldState;
for (;;) {
if (isShuttingDown()) {
return;
}
int newState;
wakeup = true;
oldState = state;
if (inEventLoop) {
newState = ST_SHUTDOWN;
} else {
switch (oldState) {
case ST_NOT_STARTED:
case ST_STARTED:
case ST_SHUTTING_DOWN:
newState = ST_SHUTDOWN;
break;
default:
newState = oldState;
wakeup = false;
}
}
if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
break;
}
}
if (ensureThreadStarted(oldState)) {
return;
}
if (wakeup) {
taskQueue.offer(WAKEUP_TASK);
if (!addTaskWakesUp) {
wakeup(inEventLoop);
}
}
}
@Override @Override
public final boolean isShuttingDown() { public final boolean isShuttingDown() {
return state >= ST_SHUTTING_DOWN; return state >= ST_SHUTTING_DOWN;
@ -732,39 +679,6 @@ public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor im
} }
} }
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
throwIfInEventLoop("invokeAny");
return super.invokeAny(tasks);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
throwIfInEventLoop("invokeAny");
return super.invokeAny(tasks, timeout, unit);
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
throwIfInEventLoop("invokeAll");
return super.invokeAll(tasks);
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
throwIfInEventLoop("invokeAll");
return super.invokeAll(tasks, timeout, unit);
}
private void throwIfInEventLoop(String method) {
if (inEventLoop()) {
throw new RejectedExecutionException("Calling " + method + " from within the EventLoop is not allowed");
}
}
/** /**
* Returns the {@link ThreadProperties} of the {@link Thread} that powers the {@link SingleThreadEventExecutor}. * Returns the {@link ThreadProperties} of the {@link Thread} that powers the {@link SingleThreadEventExecutor}.
* If the {@link SingleThreadEventExecutor} is not started yet, this operation will start it and block until * If the {@link SingleThreadEventExecutor} is not started yet, this operation will start it and block until

View File

@ -18,7 +18,7 @@ package io.netty.util.concurrent;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.List; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.RejectedExecutionHandler;
@ -36,25 +36,28 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS;
* *
* <strong>Because it provides no ordering care should be taken when using it!</strong> * <strong>Because it provides no ordering care should be taken when using it!</strong>
*/ */
public final class UnorderedThreadPoolEventExecutor extends ScheduledThreadPoolExecutor implements EventExecutor { @SuppressWarnings("unchecked")
public final class UnorderedThreadPoolEventExecutor implements EventExecutor {
private static final InternalLogger logger = InternalLoggerFactory.getInstance( private static final InternalLogger logger = InternalLoggerFactory.getInstance(
UnorderedThreadPoolEventExecutor.class); UnorderedThreadPoolEventExecutor.class);
private final Promise<?> terminationFuture = GlobalEventExecutor.INSTANCE.newPromise(); private final Promise<Void> terminationFuture = GlobalEventExecutor.INSTANCE.newPromise();
private final InnerScheduledThreadPoolExecutor executor;
/** /**
* Calls {@link UnorderedThreadPoolEventExecutor#UnorderedThreadPoolEventExecutor(int, ThreadFactory)} * Calls {@link UnorderedThreadPoolEventExecutor#UnorderedThreadPoolEventExecutor(int, ThreadFactory)}
* using {@link DefaultThreadFactory}. * using {@link DefaultThreadFactory}.
*/ */
public UnorderedThreadPoolEventExecutor(int corePoolSize) { public UnorderedThreadPoolEventExecutor(int corePoolSize) {
this(corePoolSize, new DefaultThreadFactory(UnorderedThreadPoolEventExecutor.class)); DefaultThreadFactory threadFactory = new DefaultThreadFactory(UnorderedThreadPoolEventExecutor.class);
executor = new InnerScheduledThreadPoolExecutor(this, corePoolSize, threadFactory);
} }
/** /**
* See {@link ScheduledThreadPoolExecutor#ScheduledThreadPoolExecutor(int, ThreadFactory)} * See {@link ScheduledThreadPoolExecutor#ScheduledThreadPoolExecutor(int, ThreadFactory)}
*/ */
public UnorderedThreadPoolEventExecutor(int corePoolSize, ThreadFactory threadFactory) { public UnorderedThreadPoolEventExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory); executor = new InnerScheduledThreadPoolExecutor(this, corePoolSize, threadFactory);
} }
/** /**
@ -62,7 +65,8 @@ public final class UnorderedThreadPoolEventExecutor extends ScheduledThreadPoolE
* ThreadFactory, java.util.concurrent.RejectedExecutionHandler)} using {@link DefaultThreadFactory}. * ThreadFactory, java.util.concurrent.RejectedExecutionHandler)} using {@link DefaultThreadFactory}.
*/ */
public UnorderedThreadPoolEventExecutor(int corePoolSize, RejectedExecutionHandler handler) { public UnorderedThreadPoolEventExecutor(int corePoolSize, RejectedExecutionHandler handler) {
this(corePoolSize, new DefaultThreadFactory(UnorderedThreadPoolEventExecutor.class), handler); DefaultThreadFactory threadFactory = new DefaultThreadFactory(UnorderedThreadPoolEventExecutor.class);
executor = new InnerScheduledThreadPoolExecutor(this, corePoolSize, threadFactory, handler);
} }
/** /**
@ -70,7 +74,7 @@ public final class UnorderedThreadPoolEventExecutor extends ScheduledThreadPoolE
*/ */
public UnorderedThreadPoolEventExecutor(int corePoolSize, ThreadFactory threadFactory, public UnorderedThreadPoolEventExecutor(int corePoolSize, ThreadFactory threadFactory,
RejectedExecutionHandler handler) { RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler); executor = new InnerScheduledThreadPoolExecutor(this, corePoolSize, threadFactory, handler);
} }
@Override @Override
@ -84,94 +88,97 @@ public final class UnorderedThreadPoolEventExecutor extends ScheduledThreadPoolE
} }
@Override @Override
public List<Runnable> shutdownNow() { public boolean isShutdown() {
List<Runnable> tasks = super.shutdownNow(); return executor.isShutdown();
terminationFuture.trySuccess(null);
return tasks;
} }
@Override @Override
public void shutdown() { public boolean isTerminated() {
super.shutdown(); return executor.isTerminated();
terminationFuture.trySuccess(null);
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return executor.awaitTermination(timeout, unit);
}
@Override
public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
// TODO: At the moment this just calls shutdown but we may be able to do something more smart here which // TODO: At the moment this just calls shutdown but we may be able to do something more smart here which
// respects the quietPeriod and timeout. // respects the quietPeriod and timeout.
shutdown(); executor.shutdown();
return terminationFuture(); return terminationFuture();
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return terminationFuture.asFuture(); return terminationFuture.asFuture();
} }
@Override @Override
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) { public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return runnable instanceof NonNotifyRunnable ? return (Future<Void>) executor.schedule(task, delay, unit);
task : new RunnableScheduledFutureTask<>(this, runnable, task);
} }
@Override @Override
protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return new RunnableScheduledFutureTask<>(this, callable, task); return (Future<V>) executor.schedule(task, delay, unit);
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return (ScheduledFuture<?>) super.schedule(command, delay, unit); return (Future<Void>) executor.scheduleAtFixedRate(task, initialDelay, period, unit);
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return (ScheduledFuture<V>) super.schedule(callable, delay, unit); return (Future<Void>) executor.scheduleWithFixedDelay(task, initialDelay, delay, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> submit(Runnable task) {
return (ScheduledFuture<?>) super.scheduleAtFixedRate(command, initialDelay, period, unit); return (Future<Void>) executor.submit(task);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return (ScheduledFuture<?>) super.scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
@Override
public Future<?> submit(Runnable task) {
return (Future<?>) super.submit(task);
} }
@Override @Override
public <T> Future<T> submit(Runnable task, T result) { public <T> Future<T> submit(Runnable task, T result) {
return (Future<T>) super.submit(task, result); return (Future<T>) executor.submit(task, result);
} }
@Override @Override
public <T> Future<T> submit(Callable<T> task) { public <T> Future<T> submit(Callable<T> task) {
return (Future<T>) super.submit(task); return (Future<T>) executor.submit(task);
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
super.schedule(new NonNotifyRunnable(command), 0, NANOSECONDS); executor.schedule(new NonNotifyRunnable(task), 0, NANOSECONDS);
} }
/**
* Return the task queue of the underlying {@link java.util.concurrent.Executor} instance.
* <p>
* Visible for testing.
*
* @return The task queue of this executor.
*/
BlockingQueue<Runnable> getQueue() {
return executor.getQueue();
}
/**
* Note: this class has a natural ordering that is inconsistent with equals.
*/
private static final class RunnableScheduledFutureTask<V> extends PromiseTask<V> private static final class RunnableScheduledFutureTask<V> extends PromiseTask<V>
implements RunnableScheduledFuture<V>, ScheduledFuture<V> { implements RunnableScheduledFuture<V> {
private final RunnableScheduledFuture<V> future; private final RunnableScheduledFuture<V> future;
RunnableScheduledFutureTask(EventExecutor executor, Runnable runnable, RunnableScheduledFutureTask(EventExecutor executor, Runnable runnable, RunnableScheduledFuture<V> future) {
RunnableScheduledFuture<V> future) {
super(executor, runnable, null); super(executor, runnable, null);
this.future = future; this.future = future;
} }
RunnableScheduledFutureTask(EventExecutor executor, Callable<V> callable, RunnableScheduledFutureTask(EventExecutor executor, Callable<V> callable, RunnableScheduledFuture<V> future) {
RunnableScheduledFuture<V> future) {
super(executor, callable); super(executor, callable);
this.future = future; this.future = future;
} }
@ -228,4 +235,30 @@ public final class UnorderedThreadPoolEventExecutor extends ScheduledThreadPoolE
task.run(); task.run();
} }
} }
private static final class InnerScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private final EventExecutor eventExecutor;
InnerScheduledThreadPoolExecutor(EventExecutor eventExecutor, int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
this.eventExecutor = eventExecutor;
}
InnerScheduledThreadPoolExecutor(EventExecutor eventExecutor, int corePoolSize, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler);
this.eventExecutor = eventExecutor;
}
@Override
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
return runnable instanceof NonNotifyRunnable ?
task : new RunnableScheduledFutureTask<>(eventExecutor, runnable, task);
}
@Override
protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
return new RunnableScheduledFutureTask<>(eventExecutor, callable, task);
}
}
} }

View File

@ -35,8 +35,8 @@ public class AbstractScheduledEventExecutorTest {
@Test @Test
public void testScheduleRunnableZero() { public void testScheduleRunnableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor(); TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_RUNNABLE, 0, TimeUnit.NANOSECONDS); Future<?> future = executor.schedule(TEST_RUNNABLE, 0, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS)); assertEquals(0, getDelay(future));
assertNotNull(executor.pollScheduledTask()); assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask()); assertNull(executor.pollScheduledTask());
} }
@ -44,8 +44,8 @@ public class AbstractScheduledEventExecutorTest {
@Test @Test
public void testScheduleRunnableNegative() { public void testScheduleRunnableNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor(); TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_RUNNABLE, -1, TimeUnit.NANOSECONDS); Future<?> future = executor.schedule(TEST_RUNNABLE, -1, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS)); assertEquals(0, getDelay(future));
assertNotNull(executor.pollScheduledTask()); assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask()); assertNull(executor.pollScheduledTask());
} }
@ -53,8 +53,8 @@ public class AbstractScheduledEventExecutorTest {
@Test @Test
public void testScheduleCallableZero() { public void testScheduleCallableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor(); TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_CALLABLE, 0, TimeUnit.NANOSECONDS); Future<?> future = executor.schedule(TEST_CALLABLE, 0, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS)); assertEquals(0, getDelay(future));
assertNotNull(executor.pollScheduledTask()); assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask()); assertNull(executor.pollScheduledTask());
} }
@ -62,12 +62,16 @@ public class AbstractScheduledEventExecutorTest {
@Test @Test
public void testScheduleCallableNegative() { public void testScheduleCallableNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor(); TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_CALLABLE, -1, TimeUnit.NANOSECONDS); Future<?> future = executor.schedule(TEST_CALLABLE, -1, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS)); assertEquals(0, getDelay(future));
assertNotNull(executor.pollScheduledTask()); assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask()); assertNull(executor.pollScheduledTask());
} }
private static long getDelay(Future<?> future) {
return ((RunnableScheduledFuture<?>) future).delayNanos();
}
@Test @Test
public void testScheduleAtFixedRateRunnableZero() { public void testScheduleAtFixedRateRunnableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor(); TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
@ -113,17 +117,12 @@ public class AbstractScheduledEventExecutorTest {
} }
@Override @Override
public void shutdown() { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
// NOOP
}
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -143,7 +142,7 @@ public class AbstractScheduledEventExecutorTest {
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -80,19 +80,15 @@ public class DefaultPromiseTest {
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return null; return null;
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return null; return null;
} }
@Override
public void shutdown() {
}
@Override @Override
public boolean isShutdown() { public boolean isShutdown() {
return false; return false;
@ -109,22 +105,22 @@ public class DefaultPromiseTest {
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return fail("Cannot schedule commands"); return fail("Cannot schedule commands");
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return fail("Cannot schedule commands"); return fail("Cannot schedule commands");
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return fail("Cannot schedule commands"); return fail("Cannot schedule commands");
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, public Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay,
TimeUnit unit) { TimeUnit unit) {
return fail("Cannot schedule commands"); return fail("Cannot schedule commands");
} }
@ -135,7 +131,7 @@ public class DefaultPromiseTest {
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
fail("Cannot schedule commands"); fail("Cannot schedule commands");
} }
} }
@ -145,7 +141,7 @@ public class DefaultPromiseTest {
EventExecutor executor = new RejectingEventExecutor(); EventExecutor executor = new RejectingEventExecutor();
DefaultPromise<Void> promise = new DefaultPromise<Void>(executor); DefaultPromise<Void> promise = new DefaultPromise<Void>(executor);
assertTrue(promise.cancel(false)); assertTrue(promise.cancel());
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
} }
@ -174,23 +170,33 @@ public class DefaultPromiseTest {
@Test @Test
public void testCancellationExceptionIsThrownWhenBlockingGet() throws Exception { public void testCancellationExceptionIsThrownWhenBlockingGet() throws Exception {
DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE);
assertTrue(promise.cancel(false)); assertTrue(promise.cancel());
assertThrows(CancellationException.class, promise::get); assertThrows(CancellationException.class, promise::get);
} }
@Test @Test
public void testCancellationExceptionIsThrownWhenBlockingGetWithTimeout() throws Exception { public void testCancellationExceptionIsThrownWhenBlockingGetWithTimeout() throws Exception {
DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE);
assertTrue(promise.cancel(false)); assertTrue(promise.cancel());
assertThrows(CancellationException.class, () -> promise.get(1, TimeUnit.SECONDS)); assertThrows(CancellationException.class, () -> promise.get(1, TimeUnit.SECONDS));
} }
@Test @Test
public void testCancellationExceptionIsReturnedAsCause() throws Exception { public void testCancellationExceptionIsReturnedAsCause() throws Exception {
DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE);
assertTrue(promise.cancel(false)); assertTrue(promise.cancel());
assertThat(promise.cause()).isInstanceOf(CancellationException.class); assertThat(promise.cause()).isInstanceOf(CancellationException.class);
assertTrue(promise.isFailed()); assertTrue(promise.isFailed());
assertTrue(promise.isDone());
}
@Test
public void uncancellablePromiseIsNotDone() {
DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE);
promise.setUncancellable();
assertFalse(promise.isDone());
assertFalse(promise.isCancellable());
assertFalse(promise.isCancelled());
} }
@Test @Test
@ -390,6 +396,22 @@ public class DefaultPromiseTest {
assertEquals("success", promise.getNow()); assertEquals("success", promise.getNow());
} }
@Test
public void cancellingUncancellablePromiseDoesNotCompleteIt() {
DefaultPromise<Void> promise = new DefaultPromise<>(INSTANCE);
promise.setUncancellable();
promise.cancel();
assertFalse(promise.isCancelled());
assertFalse(promise.isDone());
assertFalse(promise.isFailed());
assertFalse(promise.isSuccess());
promise.setSuccess(null);
assertFalse(promise.isCancelled());
assertTrue(promise.isDone());
assertFalse(promise.isFailed());
assertTrue(promise.isSuccess());
}
@Test @Test
public void throwUncheckedSync() throws InterruptedException { public void throwUncheckedSync() throws InterruptedException {
Exception exception = new Exception(); Exception exception = new Exception();
@ -421,7 +443,7 @@ public class DefaultPromiseTest {
@Test @Test
public void throwCancelled() throws InterruptedException { public void throwCancelled() throws InterruptedException {
DefaultPromise<String> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<String> promise = new DefaultPromise<>(INSTANCE);
promise.cancel(true); promise.cancel();
assertThrows(CancellationException.class, promise::sync); assertThrows(CancellationException.class, promise::sync);
} }

View File

@ -90,7 +90,7 @@ class FuturesTest {
public void cancelOnFutureFromMapMustCancelOriginalFuture() { public void cancelOnFutureFromMapMustCancelOriginalFuture() {
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
Future<String> strFut = promise.map(i -> i.toString()); Future<String> strFut = promise.map(i -> i.toString());
strFut.cancel(false); strFut.cancel();
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
assertTrue(strFut.isCancelled()); assertTrue(strFut.isCancelled());
} }
@ -99,7 +99,7 @@ class FuturesTest {
public void cancelOnOriginalFutureMustCancelFutureFromMap() { public void cancelOnOriginalFutureMustCancelFutureFromMap() {
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
Future<String> strFut = promise.map(i -> i.toString()); Future<String> strFut = promise.map(i -> i.toString());
promise.cancel(false); promise.cancel();
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
assertTrue(strFut.isCancelled()); assertTrue(strFut.isCancelled());
} }
@ -165,7 +165,7 @@ class FuturesTest {
public void cancelOnFutureFromFlatMapMustCancelOriginalFuture() { public void cancelOnFutureFromFlatMapMustCancelOriginalFuture() {
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString())); Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString()));
strFut.cancel(false); strFut.cancel();
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
assertTrue(strFut.isCancelled()); assertTrue(strFut.isCancelled());
} }
@ -174,7 +174,7 @@ class FuturesTest {
public void cancelOnOriginalFutureMustCancelFutureFromFlatMap() { public void cancelOnOriginalFutureMustCancelFutureFromFlatMap() {
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString())); Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString()));
promise.cancel(false); promise.cancel();
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
assertTrue(strFut.isCancelled()); assertTrue(strFut.isCancelled());
} }
@ -184,7 +184,7 @@ class FuturesTest {
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE); DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
Future<String> strFut = promise.flatMap(i -> { Future<String> strFut = promise.flatMap(i -> {
Future<String> future = new DefaultPromise<>(INSTANCE); Future<String> future = new DefaultPromise<>(INSTANCE);
future.cancel(false); future.cancel();
return future; return future;
}); });
@ -254,7 +254,7 @@ class FuturesTest {
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor); DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
promise.cascadeTo(promise2); promise.cascadeTo(promise2);
assertTrue(promise.cancel(false)); assertTrue(promise.cancel());
assertTrue(promise.isCancelled()); assertTrue(promise.isCancelled());
assertTrue(promise2.await(1, SECONDS)); assertTrue(promise2.await(1, SECONDS));
assertTrue(promise2.isCancelled()); assertTrue(promise2.isCancelled());
@ -267,7 +267,7 @@ class FuturesTest {
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor); DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
promise.cascadeTo(promise2); promise.cascadeTo(promise2);
assertTrue(promise2.cancel(false)); assertTrue(promise2.cancel());
assertTrue(promise2.isCancelled()); assertTrue(promise2.isCancelled());
// //

View File

@ -38,11 +38,7 @@ public class GlobalEventExecutorTest {
@BeforeEach @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
// Wait until the global executor is stopped (just in case there is a task running due to previous test cases) // Wait until the global executor is stopped (just in case there is a task running due to previous test cases)
for (;;) { while (e.thread != null && e.thread.isAlive()) {
if (e.thread == null || !e.thread.isAlive()) {
break;
}
Thread.sleep(50); Thread.sleep(50);
} }
} }
@ -76,7 +72,7 @@ public class GlobalEventExecutorTest {
@Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS)
public void testScheduledTasks() throws Exception { public void testScheduledTasks() throws Exception {
TestRunnable task = new TestRunnable(0); TestRunnable task = new TestRunnable(0);
ScheduledFuture<?> f = e.schedule(task, 1500, TimeUnit.MILLISECONDS); Future<?> f = e.schedule(task, 1500, TimeUnit.MILLISECONDS);
f.sync(); f.sync();
assertThat(task.ran.get(), is(true)); assertThat(task.ran.get(), is(true));
@ -115,7 +111,7 @@ public class GlobalEventExecutorTest {
//add scheduled task //add scheduled task
TestRunnable scheduledTask = new TestRunnable(0); TestRunnable scheduledTask = new TestRunnable(0);
ScheduledFuture<?> f = e.schedule(scheduledTask , 1500, TimeUnit.MILLISECONDS); Future<?> f = e.schedule(scheduledTask , 1500, TimeUnit.MILLISECONDS);
//add task //add task
TestRunnable afterTask = new TestRunnable(0); TestRunnable afterTask = new TestRunnable(0);
@ -134,7 +130,7 @@ public class GlobalEventExecutorTest {
//for https://github.com/netty/netty/issues/1614 //for https://github.com/netty/netty/issues/1614
//add scheduled task //add scheduled task
TestRunnable t = new TestRunnable(0); TestRunnable t = new TestRunnable(0);
final ScheduledFuture<?> f = e.schedule(t, 1500, TimeUnit.MILLISECONDS); Future<?> f = e.schedule(t, 1500, TimeUnit.MILLISECONDS);
//ensure always has at least one task in taskQueue //ensure always has at least one task in taskQueue
//check if scheduled tasks are triggered //check if scheduled tasks are triggered

View File

@ -17,10 +17,7 @@ package io.netty.util.concurrent;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.Queue; import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -99,82 +96,6 @@ public class SingleThreadEventExecutorTest {
executor.shutdownGracefully(); executor.shutdownGracefully();
} }
@Test
public void testInvokeAnyInEventLoop() throws Throwable {
assertTimeoutPreemptively(ofSeconds(3), () -> {
var exception = assertThrows(CompletionException.class, () -> testInvokeInEventLoop(true, false));
assertThat(exception).hasCauseInstanceOf(RejectedExecutionException.class);
});
}
@Test
public void testInvokeAnyInEventLoopWithTimeout() throws Throwable {
assertTimeoutPreemptively(ofSeconds(3), () -> {
var exception = assertThrows(CompletionException.class, () -> testInvokeInEventLoop(true, true));
assertThat(exception).hasCauseInstanceOf(RejectedExecutionException.class);
});
}
@Test
public void testInvokeAllInEventLoop() throws Throwable {
assertTimeoutPreemptively(ofSeconds(3), () -> {
var exception = assertThrows(CompletionException.class, () -> testInvokeInEventLoop(false, false));
assertThat(exception).hasCauseInstanceOf(RejectedExecutionException.class);
});
}
@Test
public void testInvokeAllInEventLoopWithTimeout() throws Throwable {
assertTimeoutPreemptively(ofSeconds(3), () -> {
var exception = assertThrows(CompletionException.class, () -> testInvokeInEventLoop(false, true));
assertThat(exception).hasCauseInstanceOf(RejectedExecutionException.class);
});
}
private static void testInvokeInEventLoop(final boolean any, final boolean timeout) {
final SingleThreadEventExecutor executor = new SingleThreadEventExecutor(Executors.defaultThreadFactory()) {
@Override
protected void run() {
while (!confirmShutdown()) {
Runnable task = takeTask();
if (task != null) {
task.run();
}
}
}
};
try {
final Promise<Void> promise = executor.newPromise();
executor.execute(() -> {
try {
Set<Callable<Boolean>> set = Collections.singleton(() -> {
promise.setFailure(new AssertionError("Should never execute the Callable"));
return Boolean.TRUE;
});
if (any) {
if (timeout) {
executor.invokeAny(set, 10, TimeUnit.SECONDS);
} else {
executor.invokeAny(set);
}
} else {
if (timeout) {
executor.invokeAll(set, 10, TimeUnit.SECONDS);
} else {
executor.invokeAll(set);
}
}
promise.setFailure(new AssertionError("Should never reach here"));
} catch (Throwable cause) {
promise.setFailure(cause);
}
});
promise.asFuture().syncUninterruptibly();
} finally {
executor.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
}
}
@Test @Test
public void testTaskAddedAfterShutdownNotAbandoned() throws Exception { public void testTaskAddedAfterShutdownNotAbandoned() throws Exception {
@ -266,7 +187,7 @@ public class SingleThreadEventExecutorTest {
//add scheduled task //add scheduled task
TestRunnable scheduledTask = new TestRunnable(); TestRunnable scheduledTask = new TestRunnable();
ScheduledFuture<?> f = executor.schedule(scheduledTask , 1500, TimeUnit.MILLISECONDS); Future<?> f = executor.schedule(scheduledTask , 1500, TimeUnit.MILLISECONDS);
//add task //add task
TestRunnable afterTask = new TestRunnable(); TestRunnable afterTask = new TestRunnable();
@ -300,7 +221,7 @@ public class SingleThreadEventExecutorTest {
assertTimeoutPreemptively(ofSeconds(5), () -> { assertTimeoutPreemptively(ofSeconds(5), () -> {
//add scheduled task //add scheduled task
TestRunnable t = new TestRunnable(); TestRunnable t = new TestRunnable();
final ScheduledFuture<?> f = executor.schedule(t, 1500, TimeUnit.MILLISECONDS); Future<?> f = executor.schedule(t, 1500, TimeUnit.MILLISECONDS);
//ensure always has at least one task in taskQueue //ensure always has at least one task in taskQueue
//check if scheduled tasks are triggered //check if scheduled tasks are triggered

View File

@ -65,7 +65,7 @@ public class UnorderedThreadPoolEventExecutorTest {
try { try {
latch.await(); latch.await();
} finally { } finally {
future.cancel(true); future.cancel();
executor.shutdownGracefully(); executor.shutdownGracefully();
} }
} }
@ -105,4 +105,41 @@ public class UnorderedThreadPoolEventExecutorTest {
executor.shutdownGracefully(); executor.shutdownGracefully();
} }
} }
@Test
public void futuresMustHaveCorrectExecutor() {
UnorderedThreadPoolEventExecutor executor = new UnorderedThreadPoolEventExecutor(1);
Runnable runnable = () -> {
};
Callable<Void> callable = () -> null;
Future<Void> future = null;
try {
future = executor.schedule(runnable, 0, TimeUnit.MILLISECONDS);
assertSame(executor, future.executor());
future.cancel();
future = executor.schedule(callable, 0, TimeUnit.MILLISECONDS);
assertSame(executor, future.executor());
future.cancel();
future = executor.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.MILLISECONDS);
assertSame(executor, future.executor());
future.cancel();
future = executor.scheduleWithFixedDelay(runnable, 0, 1, TimeUnit.MILLISECONDS);
assertSame(executor, future.executor());
future.cancel();
future = executor.submit(runnable);
assertSame(executor, future.executor());
future.cancel();
future = executor.submit(callable);
assertSame(executor, future.executor());
} finally {
future.cancel();
executor.shutdownGracefully();
}
}
} }

View File

@ -348,7 +348,7 @@ public abstract class ProxyHandler implements ChannelHandler {
private void cancelConnectTimeoutFuture() { private void cancelConnectTimeoutFuture() {
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
connectTimeoutFuture = null; connectTimeoutFuture = null;
} }
} }

View File

@ -205,7 +205,7 @@ public class FlushConsolidationHandler implements ChannelHandler {
private void cancelScheduledFlush() { private void cancelScheduledFlush() {
if (nextScheduledFlush != null) { if (nextScheduledFlush != null) {
nextScheduledFlush.cancel(false); nextScheduledFlush.cancel();
nextScheduledFlush = null; nextScheduledFlush = null;
} }
} }

View File

@ -2038,7 +2038,7 @@ public class SslHandler extends ByteToMessageDecoder {
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
// Cancel the handshake timeout when handshake is finished. // Cancel the handshake timeout when handshake is finished.
localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel(false)); localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel());
} }
private void forceFlush(ChannelHandlerContext ctx) { private void forceFlush(ChannelHandlerContext ctx) {
@ -2088,7 +2088,7 @@ public class SslHandler extends ByteToMessageDecoder {
// Close the connection if close_notify is sent in time. // Close the connection if close_notify is sent in time.
flushFuture.addListener(f -> { flushFuture.addListener(f -> {
if (timeoutFuture != null) { if (timeoutFuture != null) {
timeoutFuture.cancel(false); timeoutFuture.cancel();
} }
final long closeNotifyReadTimeout = closeNotifyReadTimeoutMillis; final long closeNotifyReadTimeout = closeNotifyReadTimeoutMillis;
if (closeNotifyReadTimeout <= 0) { if (closeNotifyReadTimeout <= 0) {
@ -2122,7 +2122,7 @@ public class SslHandler extends ByteToMessageDecoder {
// Do the close once we received the close_notify. // Do the close once we received the close_notify.
closeFuture.addListener(future -> { closeFuture.addListener(future -> {
if (closeNotifyReadTimeoutFuture != null) { if (closeNotifyReadTimeoutFuture != null) {
closeNotifyReadTimeoutFuture.cancel(false); closeNotifyReadTimeoutFuture.cancel();
} }
if (ctx.channel().isActive()) { if (ctx.channel().isActive()) {
addCloseListener(ctx.close(), promise); addCloseListener(ctx.close(), promise);

View File

@ -348,15 +348,15 @@ public class IdleStateHandler implements ChannelHandler {
state = 2; state = 2;
if (readerIdleTimeout != null) { if (readerIdleTimeout != null) {
readerIdleTimeout.cancel(false); readerIdleTimeout.cancel();
readerIdleTimeout = null; readerIdleTimeout = null;
} }
if (writerIdleTimeout != null) { if (writerIdleTimeout != null) {
writerIdleTimeout.cancel(false); writerIdleTimeout.cancel();
writerIdleTimeout = null; writerIdleTimeout = null;
} }
if (allIdleTimeout != null) { if (allIdleTimeout != null) {
allIdleTimeout.cancel(false); allIdleTimeout.cancel();
allIdleTimeout = null; allIdleTimeout = null;
} }
} }

View File

@ -117,7 +117,7 @@ public class WriteTimeoutHandler implements ChannelHandler {
lastTask = null; lastTask = null;
while (task != null) { while (task != null) {
assert task.ctx.executor().inEventLoop(); assert task.ctx.executor().inEventLoop();
task.scheduledFuture.cancel(false); task.scheduledFuture.cancel();
WriteTimeoutTask prev = task.prev; WriteTimeoutTask prev = task.prev;
task.prev = null; task.prev = null;
task.next = null; task.next = null;
@ -214,7 +214,7 @@ public class WriteTimeoutHandler implements ChannelHandler {
@Override @Override
public void operationComplete(Future<? extends Void> future) throws Exception { public void operationComplete(Future<? extends Void> future) throws Exception {
// scheduledFuture has already be set when reaching here // scheduledFuture has already be set when reaching here
scheduledFuture.cancel(false); scheduledFuture.cancel();
// Check if its safe to modify the "doubly-linked-list" that we maintain. If its not we will schedule the // Check if its safe to modify the "doubly-linked-list" that we maintain. If its not we will schedule the
// modification so its picked up by the executor.. // modification so its picked up by the executor..

View File

@ -15,13 +15,13 @@
*/ */
package io.netty.handler.traffic; package io.netty.handler.traffic;
import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler.PerChannel; import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler.PerChannel;
import io.netty.util.concurrent.EventExecutorGroup;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
/** /**
* Version for {@link GlobalChannelTrafficShapingHandler}. * Version for {@link GlobalChannelTrafficShapingHandler}.
* This TrafficCounter is the Global one, and its special property is to directly handle * This TrafficCounter is the Global one, and its special property is to directly handle
@ -36,7 +36,7 @@ public class GlobalChannelTrafficCounter extends TrafficCounter {
* @param checkInterval the checkInterval in millisecond between two computations. * @param checkInterval the checkInterval in millisecond between two computations.
*/ */
public GlobalChannelTrafficCounter(GlobalChannelTrafficShapingHandler trafficShapingHandler, public GlobalChannelTrafficCounter(GlobalChannelTrafficShapingHandler trafficShapingHandler,
ScheduledExecutorService executor, String name, long checkInterval) { EventExecutorGroup executor, String name, long checkInterval) {
super(trafficShapingHandler, executor, name, checkInterval); super(trafficShapingHandler, executor, name, checkInterval);
checkNotNullWithIAE(executor, "executor"); checkNotNullWithIAE(executor, "executor");
} }
@ -111,7 +111,7 @@ public class GlobalChannelTrafficCounter extends TrafficCounter {
resetAccounting(milliSecondFromNano()); resetAccounting(milliSecondFromNano());
trafficShapingHandler.doAccounting(this); trafficShapingHandler.doAccounting(this);
if (scheduledFuture != null) { if (scheduledFuture != null) {
scheduledFuture.cancel(true); scheduledFuture.cancel();
} }
} }

View File

@ -22,6 +22,7 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -33,7 +34,6 @@ import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -149,7 +149,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
/** /**
* Create the global TrafficCounter * Create the global TrafficCounter
*/ */
void createGlobalTrafficCounter(ScheduledExecutorService executor) { void createGlobalTrafficCounter(EventExecutorGroup executor) {
// Default // Default
setMaxDeviation(DEFAULT_DEVIATION, DEFAULT_SLOWDOWN, DEFAULT_ACCELERATION); setMaxDeviation(DEFAULT_DEVIATION, DEFAULT_SLOWDOWN, DEFAULT_ACCELERATION);
checkNotNullWithIAE(executor, "executor"); checkNotNullWithIAE(executor, "executor");
@ -167,7 +167,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeGlobalLimit * @param writeGlobalLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readGlobalLimit * @param readGlobalLimit
@ -182,7 +182,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* @param maxTime * @param maxTime
* The maximum delay to wait in case of traffic excess. * The maximum delay to wait in case of traffic excess.
*/ */
public GlobalChannelTrafficShapingHandler(ScheduledExecutorService executor, public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor,
long writeGlobalLimit, long readGlobalLimit, long writeGlobalLimit, long readGlobalLimit,
long writeChannelLimit, long readChannelLimit, long writeChannelLimit, long readChannelLimit,
long checkInterval, long maxTime) { long checkInterval, long maxTime) {
@ -196,7 +196,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeGlobalLimit * @param writeGlobalLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readGlobalLimit * @param readGlobalLimit
@ -209,7 +209,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* The delay between two computations of performances for * The delay between two computations of performances for
* channels or 0 if no stats are to be computed. * channels or 0 if no stats are to be computed.
*/ */
public GlobalChannelTrafficShapingHandler(ScheduledExecutorService executor, public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor,
long writeGlobalLimit, long readGlobalLimit, long writeGlobalLimit, long readGlobalLimit,
long writeChannelLimit, long readChannelLimit, long writeChannelLimit, long readChannelLimit,
long checkInterval) { long checkInterval) {
@ -223,7 +223,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeGlobalLimit * @param writeGlobalLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readGlobalLimit * @param readGlobalLimit
@ -233,7 +233,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* @param readChannelLimit * @param readChannelLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
*/ */
public GlobalChannelTrafficShapingHandler(ScheduledExecutorService executor, public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor,
long writeGlobalLimit, long readGlobalLimit, long writeGlobalLimit, long readGlobalLimit,
long writeChannelLimit, long readChannelLimit) { long writeChannelLimit, long readChannelLimit) {
super(writeGlobalLimit, readGlobalLimit); super(writeGlobalLimit, readGlobalLimit);
@ -246,12 +246,12 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param checkInterval * @param checkInterval
* The delay between two computations of performances for * The delay between two computations of performances for
* channels or 0 if no stats are to be computed. * channels or 0 if no stats are to be computed.
*/ */
public GlobalChannelTrafficShapingHandler(ScheduledExecutorService executor, long checkInterval) { public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor, long checkInterval) {
super(checkInterval); super(checkInterval);
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
} }
@ -260,9 +260,9 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
*/ */
public GlobalChannelTrafficShapingHandler(ScheduledExecutorService executor) { public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor) {
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
} }

View File

@ -20,6 +20,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -104,7 +105,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
/** /**
* Create the global TrafficCounter. * Create the global TrafficCounter.
*/ */
void createGlobalTrafficCounter(ScheduledExecutorService executor) { void createGlobalTrafficCounter(EventExecutorGroup executor) {
requireNonNull(executor, "executor"); requireNonNull(executor, "executor");
TrafficCounter tc = new TrafficCounter(this, executor, "GlobalTC", checkInterval); TrafficCounter tc = new TrafficCounter(this, executor, "GlobalTC", checkInterval);
setTrafficCounter(tc); setTrafficCounter(tc);
@ -120,7 +121,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* Create a new instance. * Create a new instance.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeLimit * @param writeLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readLimit * @param readLimit
@ -131,7 +132,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* @param maxTime * @param maxTime
* The maximum delay to wait in case of traffic excess. * The maximum delay to wait in case of traffic excess.
*/ */
public GlobalTrafficShapingHandler(ScheduledExecutorService executor, long writeLimit, long readLimit, public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit, long readLimit,
long checkInterval, long maxTime) { long checkInterval, long maxTime) {
super(writeLimit, readLimit, checkInterval, maxTime); super(writeLimit, readLimit, checkInterval, maxTime);
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
@ -142,7 +143,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* default max time as delay allowed value of 15000 ms. * default max time as delay allowed value of 15000 ms.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeLimit * @param writeLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readLimit * @param readLimit
@ -151,7 +152,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* The delay between two computations of performances for * The delay between two computations of performances for
* channels or 0 if no stats are to be computed. * channels or 0 if no stats are to be computed.
*/ */
public GlobalTrafficShapingHandler(ScheduledExecutorService executor, long writeLimit, public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit,
long readLimit, long checkInterval) { long readLimit, long checkInterval) {
super(writeLimit, readLimit, checkInterval); super(writeLimit, readLimit, checkInterval);
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
@ -162,13 +163,13 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* default max time as delay allowed value of 15000 ms. * default max time as delay allowed value of 15000 ms.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param writeLimit * @param writeLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
* @param readLimit * @param readLimit
* 0 or a limit in bytes/s * 0 or a limit in bytes/s
*/ */
public GlobalTrafficShapingHandler(ScheduledExecutorService executor, long writeLimit, public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit,
long readLimit) { long readLimit) {
super(writeLimit, readLimit); super(writeLimit, readLimit);
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
@ -179,12 +180,12 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
* default max time as delay allowed value of 15000 ms and no limit. * default max time as delay allowed value of 15000 ms and no limit.
* *
* @param executor * @param executor
* the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}.
* @param checkInterval * @param checkInterval
* The delay between two computations of performances for * The delay between two computations of performances for
* channels or 0 if no stats are to be computed. * channels or 0 if no stats are to be computed.
*/ */
public GlobalTrafficShapingHandler(ScheduledExecutorService executor, long checkInterval) { public GlobalTrafficShapingHandler(EventExecutorGroup executor, long checkInterval) {
super(checkInterval); super(checkInterval);
createGlobalTrafficCounter(executor); createGlobalTrafficCounter(executor);
} }

View File

@ -15,11 +15,13 @@
*/ */
package io.netty.handler.traffic; package io.netty.handler.traffic;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -148,7 +150,7 @@ public class TrafficCounter {
/** /**
* Executor that will run the monitor * Executor that will run the monitor
*/ */
final ScheduledExecutorService executor; final EventExecutorGroup executor;
/** /**
* Monitor created once in start() * Monitor created once in start()
*/ */
@ -156,7 +158,7 @@ public class TrafficCounter {
/** /**
* used in stop() to cancel the timer * used in stop() to cancel the timer
*/ */
volatile ScheduledFuture<?> scheduledFuture; volatile Future<?> scheduledFuture;
/** /**
* Is Monitor active * Is Monitor active
@ -211,7 +213,7 @@ public class TrafficCounter {
trafficShapingHandler.doAccounting(this); trafficShapingHandler.doAccounting(this);
} }
if (scheduledFuture != null) { if (scheduledFuture != null) {
scheduledFuture.cancel(true); scheduledFuture.cancel();
} }
} }
@ -252,7 +254,7 @@ public class TrafficCounter {
* @param checkInterval * @param checkInterval
* the checkInterval in millisecond between two computations. * the checkInterval in millisecond between two computations.
*/ */
public TrafficCounter(ScheduledExecutorService executor, String name, long checkInterval) { public TrafficCounter(EventExecutor executor, String name, long checkInterval) {
requireNonNull(name, "name"); requireNonNull(name, "name");
trafficShapingHandler = null; trafficShapingHandler = null;
@ -277,7 +279,7 @@ public class TrafficCounter {
* the checkInterval in millisecond between two computations. * the checkInterval in millisecond between two computations.
*/ */
public TrafficCounter( public TrafficCounter(
AbstractTrafficShapingHandler trafficShapingHandler, ScheduledExecutorService executor, AbstractTrafficShapingHandler trafficShapingHandler, EventExecutorGroup executor,
String name, long checkInterval) { String name, long checkInterval) {
this.name = requireNonNull(name, "name"); this.name = requireNonNull(name, "name");
this.trafficShapingHandler = checkNotNullWithIAE(trafficShapingHandler, "trafficShapingHandler"); this.trafficShapingHandler = checkNotNullWithIAE(trafficShapingHandler, "trafficShapingHandler");
@ -304,13 +306,12 @@ public class TrafficCounter {
public void configure(long newCheckInterval) { public void configure(long newCheckInterval) {
long newInterval = newCheckInterval / 10 * 10; long newInterval = newCheckInterval / 10 * 10;
if (checkInterval.getAndSet(newInterval) != newInterval) { if (checkInterval.getAndSet(newInterval) != newInterval) {
if (newInterval <= 0) {
stop(); stop();
if (newInterval <= 0) {
// No more active monitoring // No more active monitoring
lastTime.set(milliSecondFromNano()); lastTime.set(milliSecondFromNano());
} else { } else {
// Restart // Restart
stop();
start(); start();
} }
} }

View File

@ -30,11 +30,12 @@ import io.netty.channel.local.LocalHandler;
import io.netty.channel.local.LocalServerChannel; import io.netty.channel.local.LocalServerChannel;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.SingleThreadEventExecutor;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -42,14 +43,14 @@ import static org.junit.jupiter.api.Assertions.assertNull;
public class TrafficShapingHandlerTest { public class TrafficShapingHandlerTest {
private static final long READ_LIMIT_BYTES_PER_SECOND = 1; private static final long READ_LIMIT_BYTES_PER_SECOND = 1;
private static final ScheduledExecutorService SES = Executors.newSingleThreadScheduledExecutor(); private static final EventExecutorGroup SES = new SingleThreadEventExecutor();
private static final MultithreadEventLoopGroup GROUP = private static final MultithreadEventLoopGroup GROUP =
new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); new MultithreadEventLoopGroup(1, LocalHandler.newFactory());
@AfterAll @AfterAll
public static void destroy() { public static void destroy() {
GROUP.shutdownGracefully(); GROUP.shutdownGracefully();
SES.shutdown(); SES.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
} }
@Test @Test

View File

@ -126,7 +126,7 @@ public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark {
public void tearDown() throws Exception { public void tearDown() throws Exception {
chan.close().sync(); chan.close().sync();
serverChan.close().sync(); serverChan.close().sync();
future.cancel(true); future.cancel();
group.shutdownGracefully(0, 0, TimeUnit.SECONDS).sync(); group.shutdownGracefully(0, 0, TimeUnit.SECONDS).sync();
abyte.release(); abyte.release();
} }

View File

@ -23,8 +23,14 @@ import io.netty.channel.kqueue.KQueueHandler;
import io.netty.channel.nio.NioHandler; import io.netty.channel.nio.NioHandler;
import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.RejectedExecutionHandlers; import io.netty.util.concurrent.RejectedExecutionHandlers;
import io.netty.util.concurrent.SingleThreadEventExecutor; import io.netty.util.concurrent.SingleThreadEventExecutor;
import io.netty.util.concurrent.UnorderedThreadPoolEventExecutor;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.BenchmarkMode;
@ -38,17 +44,12 @@ import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.infra.Blackhole;
import java.util.Collection; import java.util.Collections;
import java.util.List; import java.util.Iterator;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@ -60,18 +61,17 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
* This executor is useful as the best burst latency performer because it won't go to sleep and won't be hit by the * This executor is useful as the best burst latency performer because it won't go to sleep and won't be hit by the
* cost of being awaken on both offer/consumer side. * cost of being awaken on both offer/consumer side.
*/ */
private static final class SpinExecutorService implements ExecutorService { private static final class SpinExecutorService implements EventExecutorGroup {
private static final Runnable POISON_PILL = () -> { private static final Runnable POISON_PILL = () -> {
}; };
private final Queue<Runnable> tasks; private final Queue<Runnable> tasks;
private final AtomicBoolean poisoned = new AtomicBoolean(); private final AtomicBoolean poisoned = new AtomicBoolean();
private final Thread executorThread; private final Thread executorThread;
private final Promise<Void> terminationFuture = ImmediateEventExecutor.INSTANCE.newPromise();
SpinExecutorService(int maxTasks) { SpinExecutorService(int maxTasks) {
tasks = PlatformDependent.newFixedMpscQueue(maxTasks); tasks = PlatformDependent.newFixedMpscQueue(maxTasks);
executorThread = new Thread(() -> { executorThread = new Thread(() -> {
final Queue<Runnable> tasks = SpinExecutorService.this.tasks;
Runnable task; Runnable task;
while ((task = tasks.poll()) != POISON_PILL) { while ((task = tasks.poll()) != POISON_PILL) {
if (task != null) { if (task != null) {
@ -83,22 +83,8 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
} }
@Override @Override
public void shutdown() { public boolean isShuttingDown() {
if (poisoned.compareAndSet(false, true)) { return poisoned.get();
while (!tasks.offer(POISON_PILL)) {
// Just try again
}
try {
executorThread.join();
} catch (InterruptedException e) {
//We're quite trusty :)
}
}
}
@Override
public List<Runnable> shutdownNow() {
throw new UnsupportedOperationException();
} }
@Override @Override
@ -121,46 +107,79 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public Future<Void> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override @Override
public <T> Future<T> submit(Runnable task, T result) { public <T> Future<T> submit(Runnable task, T result) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public Future<?> submit(Runnable task) { public Future<Void> submit(Runnable task) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { public void execute(Runnable task) {
throw new UnsupportedOperationException(); if (!tasks.offer(task)) {
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
throw new UnsupportedOperationException();
}
@Override
public void execute(Runnable command) {
if (!tasks.offer(command)) {
throw new RejectedExecutionException( throw new RejectedExecutionException(
"If that happens, there is something wrong with the available capacity/burst size"); "If that happens, there is something wrong with the available capacity/burst size");
} }
} }
@Override
public Future<Void> shutdownGracefully() {
if (poisoned.compareAndSet(false, true)) {
while (!tasks.offer(POISON_PILL)) {
// Just try again
}
try {
executorThread.join();
} catch (InterruptedException e) {
//We're quite trusty :)
}
}
terminationFuture.trySuccess(null);
return terminationFuture.asFuture();
}
@Override
public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return shutdownGracefully();
}
@Override
public Future<Void> terminationFuture() {
return terminationFuture.asFuture();
}
@Override
public EventExecutor next() {
throw new UnsupportedOperationException();
}
@Override
public Iterator<EventExecutor> iterator() {
return Collections.emptyIterator();
}
} }
private enum ExecutorType { private enum ExecutorType {
@ -179,8 +198,8 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
@Param({ "0", "10" }) @Param({ "0", "10" })
private int work; private int work;
private ExecutorService executor; private EventExecutorGroup executor;
private ExecutorService executorToShutdown; private EventExecutorGroup executorToShutdown;
@Setup @Setup
public void setup() { public void setup() {
@ -199,7 +218,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
executorToShutdown = executor; executorToShutdown = executor;
break; break;
case juc: case juc:
executor = Executors.newSingleThreadScheduledExecutor(); executor = new UnorderedThreadPoolEventExecutor(1);
executorToShutdown = executor; executorToShutdown = executor;
break; break;
case nioEventLoop: case nioEventLoop:
@ -230,7 +249,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
@TearDown @TearDown
public void tearDown() { public void tearDown() {
executorToShutdown.shutdown(); executorToShutdown.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
} }
@State(Scope.Thread) @State(Scope.Thread)
@ -255,7 +274,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
//benchmark is focusing on executors with a single threaded consumer: //benchmark is focusing on executors with a single threaded consumer:
//it would reduce the cost on consumer side while allowing to focus just //it would reduce the cost on consumer side while allowing to focus just
//to the threads hand-off/wake-up cost //to the threads hand-off/wake-up cost
DONE_UPDATER.lazySet(PerThreadState.this, completed + 1); DONE_UPDATER.lazySet(this, completed + 1);
}; };
} else { } else {
completeTask = () -> { completeTask = () -> {
@ -263,7 +282,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
//benchmark is focusing on executors with a single threaded consumer: //benchmark is focusing on executors with a single threaded consumer:
//it would reduce the cost on consumer side while allowing to focus just //it would reduce the cost on consumer side while allowing to focus just
//to the threads hand-off/wake-up cost //to the threads hand-off/wake-up cost
DONE_UPDATER.lazySet(PerThreadState.this, completed + 1); DONE_UPDATER.lazySet(this, completed + 1);
}; };
} }
} }
@ -283,7 +302,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
*/ */
public int spinWaitCompletionOf(int value) { public int spinWaitCompletionOf(int value) {
while (true) { while (true) {
final int lastRead = this.completed; final int lastRead = completed;
if (lastRead >= value) { if (lastRead >= value) {
return lastRead; return lastRead;
} }
@ -313,7 +332,7 @@ public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark {
} }
private int executeBurst(final PerThreadState state) { private int executeBurst(final PerThreadState state) {
final ExecutorService executor = this.executor; final EventExecutorGroup executor = this.executor;
final int burstLength = this.burstLength; final int burstLength = this.burstLength;
final Runnable completeTask = state.completeTask; final Runnable completeTask = state.completeTask;
for (int i = 0; i < burstLength; i++) { for (int i = 0; i < burstLength; i++) {

View File

@ -68,7 +68,7 @@ public class RunnableScheduledFutureAdapterBenchmark extends AbstractMicrobenchm
public Future<?> cancelInOrder(final FuturesHolder futuresHolder) { public Future<?> cancelInOrder(final FuturesHolder futuresHolder) {
return executor.submit(() -> { return executor.submit(() -> {
for (int i = 0; i < futuresHolder.num; i++) { for (int i = 0; i < futuresHolder.num; i++) {
futuresHolder.futures.get(i).cancel(false); futuresHolder.futures.get(i).cancel();
} }
}).syncUninterruptibly(); }).syncUninterruptibly();
} }
@ -77,7 +77,7 @@ public class RunnableScheduledFutureAdapterBenchmark extends AbstractMicrobenchm
public Future<?> cancelInReverseOrder(final FuturesHolder futuresHolder) { public Future<?> cancelInReverseOrder(final FuturesHolder futuresHolder) {
return executor.submit(() -> { return executor.submit(() -> {
for (int i = futuresHolder.num - 1; i >= 0; i--) { for (int i = futuresHolder.num - 1; i >= 0; i--) {
futuresHolder.futures.get(i).cancel(false); futuresHolder.futures.get(i).cancel();
} }
}).syncUninterruptibly(); }).syncUninterruptibly();
} }

View File

@ -19,15 +19,13 @@ import io.netty.channel.EventLoop;
import io.netty.util.concurrent.AbstractEventExecutor; import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import org.openjdk.jmh.annotations.Fork;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Fork;
/** /**
* This harness facilitates the sharing of an executor between JMH and Netty and * This harness facilitates the sharing of an executor between JMH and Netty and
* thus avoid measuring context switching in microbenchmarks. * thus avoid measuring context switching in microbenchmarks.
@ -87,21 +85,15 @@ public class AbstractSharedExecutorMicrobenchmark extends AbstractMicrobenchmark
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return executor.shutdownGracefully(quietPeriod, timeout, unit); return executor.shutdownGracefully(quietPeriod, timeout, unit);
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return executor.terminationFuture(); return executor.terminationFuture();
} }
@Override
@Deprecated
public void shutdown() {
executor.shutdown();
}
@Override @Override
public boolean isShuttingDown() { public boolean isShuttingDown() {
return executor.isShuttingDown(); return executor.isShuttingDown();
@ -128,8 +120,8 @@ public class AbstractSharedExecutorMicrobenchmark extends AbstractMicrobenchmark
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
executor.execute(command); executor.execute(task);
} }
@Override @Override
@ -138,24 +130,24 @@ public class AbstractSharedExecutorMicrobenchmark extends AbstractMicrobenchmark
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return executor.schedule(command, delay, unit); return executor.schedule(task, delay, unit);
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return executor.schedule(callable, delay, unit); return executor.schedule(task, delay, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { public Future<Void> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return executor.scheduleAtFixedRate(command, initialDelay, period, unit); return executor.scheduleAtFixedRate(task, initialDelay, period, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay( public Future<Void> scheduleWithFixedDelay(
Runnable command, long initialDelay, long delay, TimeUnit unit) { Runnable task, long initialDelay, long delay, TimeUnit unit) {
return executor.scheduleWithFixedDelay(command, initialDelay, delay, unit); return executor.scheduleWithFixedDelay(task, initialDelay, delay, unit);
} }
} }

View File

@ -53,7 +53,7 @@ abstract class Cache<E> {
private static final Future<?> CANCELLED_FUTURE = new Future<Object>() { private static final Future<?> CANCELLED_FUTURE = new Future<Object>() {
@Override @Override
public boolean cancel() { public boolean cancel() {
return cancel(false); return false;
} }
@Override @Override
@ -131,11 +131,6 @@ abstract class Cache<E> {
return null; return null;
} }
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override @Override
public EventExecutor executor() { public EventExecutor executor() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -352,7 +347,7 @@ abstract class Cache<E> {
break; break;
} else { } else {
// There was something else scheduled in the meantime... Cancel and try again. // There was something else scheduled in the meantime... Cancel and try again.
newFuture.cancel(true); newFuture.cancel();
} }
} else { } else {
break; break;
@ -401,7 +396,7 @@ abstract class Cache<E> {
} }
void cancel() { void cancel() {
future.cancel(false); future.cancel();
} }
@Override @Override

View File

@ -217,7 +217,7 @@ abstract class DnsQueryContext implements FutureListener<AddressedEnvelope<DnsRe
final Future<?> timeoutFuture = this.timeoutFuture; final Future<?> timeoutFuture = this.timeoutFuture;
if (timeoutFuture != null) { if (timeoutFuture != null) {
this.timeoutFuture = null; this.timeoutFuture = null;
timeoutFuture.cancel(false); timeoutFuture.cancel();
} }
// Remove the id from the manager as soon as the query completes. This may be because of success, failure or // Remove the id from the manager as soon as the query completes. This may be because of success, failure or

View File

@ -987,7 +987,7 @@ abstract class DnsResolveContext<T> {
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = i.next(); Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = i.next();
i.remove(); i.remove();
f.cancel(false); f.cancel();
} }
} }

View File

@ -41,11 +41,10 @@ import static org.junit.jupiter.api.Assertions.fail;
public abstract class AbstractSingleThreadEventLoopTest { public abstract class AbstractSingleThreadEventLoopTest {
@Test @Test
@SuppressWarnings("deprecation")
public void shutdownBeforeStart() throws Exception { public void shutdownBeforeStart() throws Exception {
EventLoopGroup group = new MultithreadEventLoopGroup(newIoHandlerFactory()); EventLoopGroup group = new MultithreadEventLoopGroup(newIoHandlerFactory());
assertFalse(group.awaitTermination(2, TimeUnit.MILLISECONDS)); assertFalse(group.awaitTermination(2, TimeUnit.MILLISECONDS));
group.shutdown(); group.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
assertTrue(group.awaitTermination(200, TimeUnit.MILLISECONDS)); assertTrue(group.awaitTermination(200, TimeUnit.MILLISECONDS));
} }

View File

@ -58,11 +58,11 @@ public class SocketCancelWriteTest extends AbstractSocketTest {
Channel cc = cb.connect(sc.localAddress()).get(); Channel cc = cb.connect(sc.localAddress()).get();
Future<Void> f = cc.write(a); Future<Void> f = cc.write(a);
assertTrue(f.cancel(false)); assertTrue(f.cancel());
cc.writeAndFlush(b); cc.writeAndFlush(b);
cc.write(c); cc.write(c);
Future<Void> f2 = cc.write(d); Future<Void> f2 = cc.write(d);
assertTrue(f2.cancel(false)); assertTrue(f2.cancel());
cc.writeAndFlush(e); cc.writeAndFlush(e);
while (sh.counter < 3) { while (sh.counter < 3) {

View File

@ -137,7 +137,7 @@ public class SocketConnectionAttemptTest extends AbstractClientSocketTest {
} }
} }
if (future.cancel(true)) { if (future.cancel()) {
assertThat(future.isCancelled()).isTrue(); assertThat(future.isCancelled()).isTrue();
} else { } else {
// Cancellation not supported by the transport. // Cancellation not supported by the transport.

View File

@ -166,7 +166,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
Future<?> future = connectTimeoutFuture; Future<?> future = connectTimeoutFuture;
if (future != null) { if (future != null) {
future.cancel(false); future.cancel();
connectTimeoutFuture = null; connectTimeoutFuture = null;
} }
@ -612,7 +612,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
promise.asFuture().addListener(future -> { promise.asFuture().addListener(future -> {
if (future.isCancelled()) { if (future.isCancelled()) {
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
close(newPromise()); close(newPromise());
@ -684,7 +684,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
// See https://github.com/netty/netty/issues/1770 // See https://github.com/netty/netty/issues/1770
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
} }

View File

@ -61,7 +61,7 @@ public class EpollEventLoopTest extends AbstractSingleThreadEventLoopTest {
}, Long.MAX_VALUE, TimeUnit.MILLISECONDS); }, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
assertFalse(future.awaitUninterruptibly(1000)); assertFalse(future.awaitUninterruptibly(1000));
assertTrue(future.cancel(true)); assertTrue(future.cancel());
assertNull(capture.get()); assertNull(capture.get());
} finally { } finally {
group.shutdownGracefully(); group.shutdownGracefully();

View File

@ -563,7 +563,7 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan
promise.asFuture().addListener(future -> { promise.asFuture().addListener(future -> {
if (future.isCancelled()) { if (future.isCancelled()) {
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
close(newPromise()); close(newPromise());
@ -635,7 +635,7 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
// See https://github.com/netty/netty/issues/1770 // See https://github.com/netty/netty/issues/1770
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
} }

View File

@ -41,7 +41,7 @@ public class KQueueEventLoopTest extends AbstractSingleThreadEventLoopTest {
}, Long.MAX_VALUE, TimeUnit.MILLISECONDS); }, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
assertFalse(future.awaitUninterruptibly(1000)); assertFalse(future.awaitUninterruptibly(1000));
assertTrue(future.cancel(true)); assertTrue(future.cancel());
group.shutdownGracefully(); group.shutdownGracefully();
} }

View File

@ -87,6 +87,7 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparabl
/** /**
* Return the {@link EventLoop} this {@link Channel} was registered to. * Return the {@link EventLoop} this {@link Channel} was registered to.
*/ */
@Override
EventLoop executor(); EventLoop executor();
/** /**

View File

@ -45,7 +45,7 @@ public class SingleThreadEventLoop extends SingleThreadEventExecutor implements
@Override @Override
public boolean canBlock() { public boolean canBlock() {
assert inEventLoop(); assert inEventLoop();
return !SingleThreadEventLoop.this.hasTasks() && !SingleThreadEventLoop.this.hasScheduledTasks(); return !hasTasks() && !hasScheduledTasks();
} }
@Override @Override

View File

@ -63,9 +63,9 @@ final class EmbeddedEventLoop extends AbstractScheduledEventExecutor implements
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
requireNonNull(command, "command"); requireNonNull(task, "command");
tasks.add(command); tasks.add(task);
if (!running) { if (!running) {
runTasks(); runTasks();
} }
@ -124,18 +124,12 @@ final class EmbeddedEventLoop extends AbstractScheduledEventExecutor implements
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
throw new UnsupportedOperationException();
}
@Override
@Deprecated
public void shutdown() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -250,7 +250,7 @@ public abstract class AbstractNioChannel extends AbstractChannel {
promise.asFuture().addListener(future -> { promise.asFuture().addListener(future -> {
if (future.isCancelled()) { if (future.isCancelled()) {
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
close(newPromise()); close(newPromise());
@ -317,7 +317,7 @@ public abstract class AbstractNioChannel extends AbstractChannel {
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
// See https://github.com/netty/netty/issues/1770 // See https://github.com/netty/netty/issues/1770
if (connectTimeoutFuture != null) { if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false); connectTimeoutFuture.cancel();
} }
connectPromise = null; connectPromise = null;
} }
@ -452,7 +452,7 @@ public abstract class AbstractNioChannel extends AbstractChannel {
Future<?> future = connectTimeoutFuture; Future<?> future = connectTimeoutFuture;
if (future != null) { if (future != null) {
future.cancel(false); future.cancel();
connectTimeoutFuture = null; connectTimeoutFuture = null;
} }
} }

View File

@ -17,7 +17,6 @@ package io.netty.channel;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -26,6 +25,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import static io.netty.util.concurrent.ImmediateEventExecutor.INSTANCE;
import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
@ -44,6 +44,7 @@ public class AbstractChannelTest {
// This allows us to have a single-threaded test // This allows us to have a single-threaded test
when(eventLoop.inEventLoop()).thenReturn(true); when(eventLoop.inEventLoop()).thenReturn(true);
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class)); when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
when(eventLoop.newPromise()).thenReturn(INSTANCE.newPromise());
TestChannel channel = new TestChannel(eventLoop); TestChannel channel = new TestChannel(eventLoop);
// Using spy as otherwise intelliJ will not be able to understand that we dont want to skip the handler // Using spy as otherwise intelliJ will not be able to understand that we dont want to skip the handler
@ -78,7 +79,7 @@ public class AbstractChannelTest {
// This allows us to have a single-threaded test // This allows us to have a single-threaded test
when(eventLoop.inEventLoop()).thenReturn(true); when(eventLoop.inEventLoop()).thenReturn(true);
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class)); when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
when(eventLoop.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise()); when(eventLoop.newPromise()).thenAnswer(inv -> INSTANCE.newPromise());
doAnswer(invocationOnMock -> { doAnswer(invocationOnMock -> {
((Runnable) invocationOnMock.getArgument(0)).run(); ((Runnable) invocationOnMock.getArgument(0)).run();
@ -134,7 +135,7 @@ public class AbstractChannelTest {
// This allows us to have a single-threaded test // This allows us to have a single-threaded test
when(eventLoop.inEventLoop()).thenReturn(true); when(eventLoop.inEventLoop()).thenReturn(true);
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class)); when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
when(eventLoop.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise()); when(eventLoop.newPromise()).thenAnswer(inv -> INSTANCE.newPromise());
doAnswer(invocationOnMock -> { doAnswer(invocationOnMock -> {
((Runnable) invocationOnMock.getArgument(0)).run(); ((Runnable) invocationOnMock.getArgument(0)).run();
return null; return null;

View File

@ -38,7 +38,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -95,14 +94,12 @@ public class SingleThreadEventLoopTest {
} }
@Test @Test
@SuppressWarnings("deprecation")
public void shutdownBeforeStart() throws Exception { public void shutdownBeforeStart() throws Exception {
loopA.shutdown(); loopA.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS).await();
assertRejection(loopA); assertRejection(loopA);
} }
@Test @Test
@SuppressWarnings("deprecation")
public void shutdownAfterStart() throws Exception { public void shutdownAfterStart() throws Exception {
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
loopA.execute(latch::countDown); loopA.execute(latch::countDown);
@ -111,7 +108,7 @@ public class SingleThreadEventLoopTest {
latch.await(); latch.await();
// Request the event loop thread to stop. // Request the event loop thread to stop.
loopA.shutdown(); loopA.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS).await();
assertRejection(loopA); assertRejection(loopA);
assertTrue(loopA.isShutdown()); assertTrue(loopA.isShutdown());
@ -165,7 +162,7 @@ public class SingleThreadEventLoopTest {
final Queue<Long> timestamps = new LinkedBlockingQueue<>(); final Queue<Long> timestamps = new LinkedBlockingQueue<>();
final int expectedTimeStamps = 5; final int expectedTimeStamps = 5;
final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps); final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps);
ScheduledFuture<?> f = loopA.scheduleAtFixedRate(() -> { Future<?> f = loopA.scheduleAtFixedRate(() -> {
timestamps.add(System.nanoTime()); timestamps.add(System.nanoTime());
try { try {
Thread.sleep(50); Thread.sleep(50);
@ -175,13 +172,13 @@ public class SingleThreadEventLoopTest {
allTimeStampsLatch.countDown(); allTimeStampsLatch.countDown();
}, 100, 100, TimeUnit.MILLISECONDS); }, 100, 100, TimeUnit.MILLISECONDS);
allTimeStampsLatch.await(); allTimeStampsLatch.await();
assertTrue(f.cancel(true)); assertTrue(f.cancel());
Thread.sleep(300); Thread.sleep(300);
assertEquals(expectedTimeStamps, timestamps.size()); assertEquals(expectedTimeStamps, timestamps.size());
// Check if the task was run without a lag. // Check if the task was run without a lag.
Long firstTimestamp = null; Long firstTimestamp = null;
int cnt = 0; long cnt = 0;
for (Long t: timestamps) { for (Long t: timestamps) {
if (firstTimestamp == null) { if (firstTimestamp == null) {
firstTimestamp = t; firstTimestamp = t;
@ -212,7 +209,7 @@ public class SingleThreadEventLoopTest {
final Queue<Long> timestamps = new LinkedBlockingQueue<>(); final Queue<Long> timestamps = new LinkedBlockingQueue<>();
final int expectedTimeStamps = 5; final int expectedTimeStamps = 5;
final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps); final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps);
ScheduledFuture<?> f = loopA.scheduleAtFixedRate(() -> { Future<?> f = loopA.scheduleAtFixedRate(() -> {
boolean empty = timestamps.isEmpty(); boolean empty = timestamps.isEmpty();
timestamps.add(System.nanoTime()); timestamps.add(System.nanoTime());
if (empty) { if (empty) {
@ -225,7 +222,7 @@ public class SingleThreadEventLoopTest {
allTimeStampsLatch.countDown(); allTimeStampsLatch.countDown();
}, 100, 100, TimeUnit.MILLISECONDS); }, 100, 100, TimeUnit.MILLISECONDS);
allTimeStampsLatch.await(); allTimeStampsLatch.await();
assertTrue(f.cancel(true)); assertTrue(f.cancel());
Thread.sleep(300); Thread.sleep(300);
assertEquals(expectedTimeStamps, timestamps.size()); assertEquals(expectedTimeStamps, timestamps.size());
@ -265,7 +262,7 @@ public class SingleThreadEventLoopTest {
final Queue<Long> timestamps = new LinkedBlockingQueue<>(); final Queue<Long> timestamps = new LinkedBlockingQueue<>();
final int expectedTimeStamps = 3; final int expectedTimeStamps = 3;
final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps); final CountDownLatch allTimeStampsLatch = new CountDownLatch(expectedTimeStamps);
ScheduledFuture<?> f = loopA.scheduleWithFixedDelay(() -> { Future<?> f = loopA.scheduleWithFixedDelay(() -> {
timestamps.add(System.nanoTime()); timestamps.add(System.nanoTime());
try { try {
Thread.sleep(51); Thread.sleep(51);
@ -275,7 +272,7 @@ public class SingleThreadEventLoopTest {
allTimeStampsLatch.countDown(); allTimeStampsLatch.countDown();
}, 100, 100, TimeUnit.MILLISECONDS); }, 100, 100, TimeUnit.MILLISECONDS);
allTimeStampsLatch.await(); allTimeStampsLatch.await();
assertTrue(f.cancel(true)); assertTrue(f.cancel());
Thread.sleep(300); Thread.sleep(300);
assertEquals(expectedTimeStamps, timestamps.size()); assertEquals(expectedTimeStamps, timestamps.size());
@ -294,7 +291,6 @@ public class SingleThreadEventLoopTest {
} }
@Test @Test
@SuppressWarnings("deprecation")
public void shutdownWithPendingTasks() throws Exception { public void shutdownWithPendingTasks() throws Exception {
final int NUM_TASKS = 3; final int NUM_TASKS = 3;
final AtomicInteger ranTasks = new AtomicInteger(); final AtomicInteger ranTasks = new AtomicInteger();
@ -321,7 +317,7 @@ public class SingleThreadEventLoopTest {
assertEquals(1, ranTasks.get()); assertEquals(1, ranTasks.get());
// Shut down the event loop to test if the other tasks are run before termination. // Shut down the event loop to test if the other tasks are run before termination.
loopA.shutdown(); loopA.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
// Let the other tasks run. // Let the other tasks run.
latch.countDown(); latch.countDown();
@ -337,9 +333,8 @@ public class SingleThreadEventLoopTest {
@Test @Test
@Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
@SuppressWarnings("deprecation")
public void testRegistrationAfterShutdown() throws Exception { public void testRegistrationAfterShutdown() throws Exception {
loopA.shutdown(); loopA.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS).await();
// Disable logging temporarily. // Disable logging temporarily.
Logger root = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); Logger root = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
@ -367,9 +362,8 @@ public class SingleThreadEventLoopTest {
@Test @Test
@Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
@SuppressWarnings("deprecation")
public void testRegistrationAfterShutdown2() throws Exception { public void testRegistrationAfterShutdown2() throws Exception {
loopA.shutdown(); loopA.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS).await();
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
Channel ch = new LocalChannel(loopA); Channel ch = new LocalChannel(loopA);
@ -462,17 +456,14 @@ public class SingleThreadEventLoopTest {
@Override @Override
protected void run() { protected void run() {
for (;;) { do {
Runnable task = takeTask(); Runnable task = takeTask();
if (task != null) { if (task != null) {
task.run(); task.run();
updateLastExecutionTime(); updateLastExecutionTime();
} }
if (confirmShutdown()) { } while (!confirmShutdown());
break;
}
}
} }
@Override @Override
@ -504,7 +495,7 @@ public class SingleThreadEventLoopTest {
@Override @Override
protected void run() { protected void run() {
for (;;) { do {
try { try {
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayNanos(System.nanoTime()))); Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayNanos(System.nanoTime())));
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -513,10 +504,7 @@ public class SingleThreadEventLoopTest {
runAllTasks(Integer.MAX_VALUE); runAllTasks(Integer.MAX_VALUE);
if (confirmShutdown()) { } while (!confirmShutdown());
break;
}
}
} }
@Override @Override

View File

@ -98,7 +98,7 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
}, Long.MAX_VALUE, TimeUnit.MILLISECONDS); }, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
assertFalse(future.awaitUninterruptibly(1000)); assertFalse(future.awaitUninterruptibly(1000));
assertTrue(future.cancel(true)); assertTrue(future.cancel());
group.shutdownGracefully(); group.shutdownGracefully();
} }
@ -169,7 +169,6 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
} }
} }
@SuppressWarnings("deprecation")
@Test @Test
public void testTaskRemovalOnShutdownThrowsNoUnsupportedOperationException() throws Exception { public void testTaskRemovalOnShutdownThrowsNoUnsupportedOperationException() throws Exception {
final AtomicReference<Throwable> error = new AtomicReference<>(); final AtomicReference<Throwable> error = new AtomicReference<>();
@ -191,9 +190,9 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
} }
}); });
t.start(); t.start();
group.shutdownNow(); Future<?> termination = group.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
t.join(); t.join();
group.terminationFuture().syncUninterruptibly(); termination.syncUninterruptibly();
assertThat(error.get(), instanceOf(RejectedExecutionException.class)); assertThat(error.get(), instanceOf(RejectedExecutionException.class));
error.set(null); error.set(null);
} }

View File

@ -23,7 +23,6 @@ import io.netty.channel.nio.AbstractNioChannel;
import io.netty.channel.nio.NioHandler; import io.netty.channel.nio.NioHandler;
import io.netty.util.concurrent.AbstractEventExecutor; import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ScheduledFuture;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
@ -108,6 +107,7 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
this.eventLoop = eventLoop; this.eventLoop = eventLoop;
} }
@Override
@Test @Test
public EventLoop next() { public EventLoop next() {
return this; return this;
@ -118,11 +118,6 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
return eventLoop.unsafe(); return eventLoop.unsafe();
} }
@Override
public void shutdown() {
eventLoop.shutdown();
}
@Override @Override
public boolean inEventLoop(Thread thread) { public boolean inEventLoop(Thread thread) {
return eventLoop.inEventLoop(thread); return eventLoop.inEventLoop(thread);
@ -134,12 +129,12 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
} }
@Override @Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { public Future<Void> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
return eventLoop.shutdownGracefully(quietPeriod, timeout, unit); return eventLoop.shutdownGracefully(quietPeriod, timeout, unit);
} }
@Override @Override
public Future<?> terminationFuture() { public Future<Void> terminationFuture() {
return eventLoop.terminationFuture(); return eventLoop.terminationFuture();
} }
@ -159,30 +154,30 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
} }
@Override @Override
public void execute(Runnable command) { public void execute(Runnable task) {
eventLoop.execute(command); eventLoop.execute(task);
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { public Future<Void> schedule(Runnable task, long delay, TimeUnit unit) {
return eventLoop.schedule(command, delay, unit); return eventLoop.schedule(task, delay, unit);
} }
@Override @Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { public <V> Future<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return eventLoop.schedule(callable, delay, unit); return eventLoop.schedule(task, delay, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate( public Future<Void> scheduleAtFixedRate(
Runnable command, long initialDelay, long period, TimeUnit unit) { Runnable task, long initialDelay, long period, TimeUnit unit) {
return eventLoop.scheduleAtFixedRate(command, initialDelay, period, unit); return eventLoop.scheduleAtFixedRate(task, initialDelay, period, unit);
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay( public Future<Void> scheduleWithFixedDelay(
Runnable command, long initialDelay, long delay, TimeUnit unit) { Runnable task, long initialDelay, long delay, TimeUnit unit) {
return eventLoop.scheduleWithFixedDelay(command, initialDelay, delay, unit); return eventLoop.scheduleWithFixedDelay(task, initialDelay, delay, unit);
} }
} }