343 lines
13 KiB
Java
343 lines
13 KiB
Java
/*
|
|
* Copyright 2012 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:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.channel;
|
|
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* The result of an asynchronous {@link Channel} I/O operation.
|
|
* <p>
|
|
* All I/O operations in Netty are asynchronous. It means any I/O calls will
|
|
* return immediately with no guarantee that the requested I/O operation has
|
|
* been completed at the end of the call. Instead, you will be returned with
|
|
* a {@link ChannelFuture} instance which gives you the information about the
|
|
* result or status of the I/O operation.
|
|
* <p>
|
|
* A {@link ChannelFuture} is either <em>uncompleted</em> or <em>completed</em>.
|
|
* When an I/O operation begins, a new future object is created. The new future
|
|
* is uncompleted initially - it is neither succeeded, failed, nor cancelled
|
|
* because the I/O operation is not finished yet. If the I/O operation is
|
|
* finished either successfully, with failure, or by cancellation, the future is
|
|
* marked as completed with more specific information, such as the cause of the
|
|
* failure. Please note that even failure and cancellation belong to the
|
|
* completed state.
|
|
* <pre>
|
|
* +---------------------------+
|
|
* | Completed successfully |
|
|
* +---------------------------+
|
|
* +----> isDone() = <b>true</b> |
|
|
* +--------------------------+ | | isSuccess() = <b>true</b> |
|
|
* | Uncompleted | | +===========================+
|
|
* +--------------------------+ | | Completed with failure |
|
|
* | isDone() = <b>false</b> | | +---------------------------+
|
|
* | isSuccess() = false |----+----> isDone() = <b>true</b> |
|
|
* | isCancelled() = false | | | getCause() = <b>non-null</b> |
|
|
* | getCause() = null | | +===========================+
|
|
* +--------------------------+ | | Completed by cancellation |
|
|
* | +---------------------------+
|
|
* +----> isDone() = <b>true</b> |
|
|
* | isCancelled() = <b>true</b> |
|
|
* +---------------------------+
|
|
* </pre>
|
|
*
|
|
* Various methods are provided to let you check if the I/O operation has been
|
|
* completed, wait for the completion, and retrieve the result of the I/O
|
|
* operation. It also allows you to add {@link ChannelFutureListener}s so you
|
|
* can get notified when the I/O operation is completed.
|
|
*
|
|
* <h3>Prefer {@link #addListener(ChannelFutureListener)} to {@link #await()}</h3>
|
|
*
|
|
* It is recommended to prefer {@link #addListener(ChannelFutureListener)} to
|
|
* {@link #await()} wherever possible to get notified when an I/O operation is
|
|
* done and to do any follow-up tasks.
|
|
* <p>
|
|
* {@link #addListener(ChannelFutureListener)} is non-blocking. It simply adds
|
|
* the specified {@link ChannelFutureListener} to the {@link ChannelFuture}, and
|
|
* I/O thread will notify the listeners when the I/O operation associated with
|
|
* the future is done. {@link ChannelFutureListener} yields the best
|
|
* performance and resource utilization because it does not block at all, but
|
|
* it could be tricky to implement a sequential logic if you are not used to
|
|
* event-driven programming.
|
|
* <p>
|
|
* By contrast, {@link #await()} is a blocking operation. Once called, the
|
|
* caller thread blocks until the operation is done. It is easier to implement
|
|
* a sequential logic with {@link #await()}, but the caller thread blocks
|
|
* unnecessarily until the I/O operation is done and there's relatively
|
|
* expensive cost of inter-thread notification. Moreover, there's a chance of
|
|
* dead lock in a particular circumstance, which is described below.
|
|
*
|
|
* <h3>Do not call {@link #await()} inside {@link ChannelHandler}</h3>
|
|
* <p>
|
|
* The event handler methods in {@link ChannelHandler} is usually called by
|
|
* an I/O thread. If {@link #await()} is called by an event handler
|
|
* method, which is called by the I/O thread, the I/O operation it is waiting
|
|
* for might never be complete because {@link #await()} can block the I/O
|
|
* operation it is waiting for, which is a dead lock.
|
|
* <pre>
|
|
* // BAD - NEVER DO THIS
|
|
* {@code @Override}
|
|
* public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {
|
|
* if (e.getMessage() instanceof GoodByeMessage) {
|
|
* {@link ChannelFuture} future = e.getChannel().close();
|
|
* future.awaitUninterruptibly();
|
|
* // Perform post-closure operation
|
|
* // ...
|
|
* }
|
|
* }
|
|
*
|
|
* // GOOD
|
|
* {@code @Override}
|
|
* public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {
|
|
* if (e.getMessage() instanceof GoodByeMessage) {
|
|
* {@link ChannelFuture} future = e.getChannel().close();
|
|
* future.addListener(new {@link ChannelFutureListener}() {
|
|
* public void operationComplete({@link ChannelFuture} future) {
|
|
* // Perform post-closure operation
|
|
* // ...
|
|
* }
|
|
* });
|
|
* }
|
|
* }
|
|
* </pre>
|
|
* <p>
|
|
* In spite of the disadvantages mentioned above, there are certainly the cases
|
|
* where it is more convenient to call {@link #await()}. In such a case, please
|
|
* make sure you do not call {@link #await()} in an I/O thread. Otherwise,
|
|
* {@link IllegalStateException} will be raised to prevent a dead lock.
|
|
*
|
|
* <h3>Do not confuse I/O timeout and await timeout</h3>
|
|
*
|
|
* The timeout value you specify with {@link #await(long)},
|
|
* {@link #await(long, TimeUnit)}, {@link #awaitUninterruptibly(long)}, or
|
|
* {@link #awaitUninterruptibly(long, TimeUnit)} are not related with I/O
|
|
* timeout at all. If an I/O operation times out, the future will be marked as
|
|
* 'completed with failure,' as depicted in the diagram above. For example,
|
|
* connect timeout should be configured via a transport-specific option:
|
|
* <pre>
|
|
* // BAD - NEVER DO THIS
|
|
* {@link ClientBootstrap} b = ...;
|
|
* {@link ChannelFuture} f = b.connect(...);
|
|
* f.awaitUninterruptibly(10, TimeUnit.SECONDS);
|
|
* if (f.isCancelled()) {
|
|
* // Connection attempt cancelled by user
|
|
* } else if (!f.isSuccess()) {
|
|
* // You might get a NullPointerException here because the future
|
|
* // might not be completed yet.
|
|
* f.getCause().printStackTrace();
|
|
* } else {
|
|
* // Connection established successfully
|
|
* }
|
|
*
|
|
* // GOOD
|
|
* {@link ClientBootstrap} b = ...;
|
|
* // Configure the connect timeout option.
|
|
* <b>b.setOption("connectTimeoutMillis", 10000);</b>
|
|
* {@link ChannelFuture} f = b.connect(...);
|
|
* f.awaitUninterruptibly();
|
|
*
|
|
* // Now we are sure the future is completed.
|
|
* assert f.isDone();
|
|
*
|
|
* if (f.isCancelled()) {
|
|
* // Connection attempt cancelled by user
|
|
* } else if (!f.isSuccess()) {
|
|
* f.getCause().printStackTrace();
|
|
* } else {
|
|
* // Connection established successfully
|
|
* }
|
|
* </pre>
|
|
* @apiviz.landmark
|
|
* @apiviz.owns io.netty.channel.ChannelFutureListener - - notifies
|
|
*/
|
|
public interface ChannelFuture extends Future<Void> {
|
|
|
|
/**
|
|
* Returns a channel where the I/O operation associated with this
|
|
* future takes place.
|
|
*/
|
|
Channel channel();
|
|
|
|
/**
|
|
* Returns {@code true} if and only if this future is
|
|
* complete, regardless of whether the operation was successful, failed,
|
|
* or cancelled.
|
|
*/
|
|
@Override
|
|
boolean isDone();
|
|
|
|
/**
|
|
* Returns {@code true} if and only if this future was
|
|
* cancelled by a {@link #cancel()} method.
|
|
*/
|
|
@Override
|
|
boolean isCancelled();
|
|
|
|
/**
|
|
* Returns {@code true} if and only if the I/O operation was completed
|
|
* successfully.
|
|
*/
|
|
boolean isSuccess();
|
|
|
|
/**
|
|
* Returns the cause of the failed I/O operation if the I/O operation has
|
|
* failed.
|
|
*
|
|
* @return the cause of the failure.
|
|
* {@code null} if succeeded or this future is not
|
|
* completed yet.
|
|
*/
|
|
Throwable cause();
|
|
|
|
/**
|
|
* Cancels the I/O operation associated with this future
|
|
* and notifies all listeners if canceled successfully.
|
|
*
|
|
* @return {@code true} if and only if the operation has been canceled.
|
|
* {@code false} if the operation can't be canceled or is already
|
|
* completed.
|
|
*/
|
|
boolean cancel();
|
|
|
|
/**
|
|
* Marks this future as a success and notifies all
|
|
* listeners.
|
|
*
|
|
* @return {@code true} if and only if successfully marked this future as
|
|
* a success. Otherwise {@code false} because this future is
|
|
* already marked as either a success or a failure.
|
|
*/
|
|
boolean setSuccess();
|
|
|
|
/**
|
|
* Marks this future as a failure and notifies all
|
|
* listeners.
|
|
*
|
|
* @return {@code true} if and only if successfully marked this future as
|
|
* a failure. Otherwise {@code false} because this future is
|
|
* already marked as either a success or a failure.
|
|
*/
|
|
boolean setFailure(Throwable cause);
|
|
|
|
/**
|
|
* Notifies the progress of the operation to the listeners that implements
|
|
* {@link ChannelFutureProgressListener}. Please note that this method will
|
|
* not do anything and return {@code false} if this future is complete
|
|
* already.
|
|
*
|
|
* @return {@code true} if and only if notification was made.
|
|
*/
|
|
boolean setProgress(long amount, long current, long total);
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
ChannelFuture addListener(ChannelFutureListener listener);
|
|
|
|
/**
|
|
* Removes the specified listener from this future.
|
|
* The specified listener is no longer notified when this
|
|
* future is {@linkplain #isDone() done}. If the specified
|
|
* listener is not associated with this future, this method
|
|
* does nothing and returns silently.
|
|
*/
|
|
ChannelFuture removeListener(ChannelFutureListener listener);
|
|
|
|
/**
|
|
* Waits for this future until it is done, and rethrows the cause of the failure if this future
|
|
* failed. If the cause of the failure is a checked exception, it is wrapped with a new
|
|
* {@link ChannelException} before being thrown.
|
|
*/
|
|
ChannelFuture sync() throws InterruptedException;
|
|
|
|
/**
|
|
* Waits for this future until it is done, and rethrows the cause of the failure if this future
|
|
* failed. If the cause of the failure is a checked exception, it is wrapped with a new
|
|
* {@link ChannelException} before being thrown.
|
|
*/
|
|
ChannelFuture syncUninterruptibly();
|
|
|
|
/**
|
|
* Waits for this future to be completed.
|
|
*
|
|
* @throws InterruptedException
|
|
* if the current thread was interrupted
|
|
*/
|
|
ChannelFuture await() throws InterruptedException;
|
|
|
|
/**
|
|
* Waits for this future to be completed without
|
|
* interruption. This method catches an {@link InterruptedException} and
|
|
* discards it silently.
|
|
*/
|
|
ChannelFuture awaitUninterruptibly();
|
|
|
|
/**
|
|
* Waits for this future to be completed within the
|
|
* specified time limit.
|
|
*
|
|
* @return {@code true} if and only if the future was completed within
|
|
* the specified time limit
|
|
*
|
|
* @throws InterruptedException
|
|
* if the current thread was interrupted
|
|
*/
|
|
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
|
|
|
|
/**
|
|
* Waits for this future to be completed within the
|
|
* specified time limit.
|
|
*
|
|
* @return {@code true} if and only if the future was completed within
|
|
* the specified time limit
|
|
*
|
|
* @throws InterruptedException
|
|
* if the current thread was interrupted
|
|
*/
|
|
boolean await(long timeoutMillis) throws InterruptedException;
|
|
|
|
/**
|
|
* Waits for this future to be completed within the
|
|
* specified time limit without interruption. This method catches an
|
|
* {@link InterruptedException} and discards it silently.
|
|
*
|
|
* @return {@code true} if and only if the future was completed within
|
|
* the specified time limit
|
|
*/
|
|
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
|
|
|
|
/**
|
|
* Waits for this future to be completed within the
|
|
* specified time limit without interruption. This method catches an
|
|
* {@link InterruptedException} and discards it silently.
|
|
*
|
|
* @return {@code true} if and only if the future was completed within
|
|
* the specified time limit
|
|
*/
|
|
boolean awaitUninterruptibly(long timeoutMillis);
|
|
|
|
/**
|
|
* A {@link ChannelFuture} which is not allowed to be sent to {@link ChannelPipeline} due to
|
|
* implementation details.
|
|
*/
|
|
interface Unsafe extends ChannelFuture {
|
|
// Tag interface
|
|
}
|
|
}
|