Let ChannelGroupFuture extends ChannelFuture and ChannelGroupFutureListener GenericFutureListener

This commit is contained in:
Norman Maurer 2013-03-11 08:38:56 +01:00
parent f2a35273e6
commit 17ebbdec20
11 changed files with 656 additions and 389 deletions

View File

@ -0,0 +1,74 @@
/*
* 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:
*
* 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.util.concurrent;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor {
private final Future succeededFuture = new SucceededFuture(this);
private final TaskScheduler scheduler;
protected AbstractEventExecutor(TaskScheduler scheduler) {
if (scheduler == null) {
throw new NullPointerException("scheduler");
}
this.scheduler = scheduler;
}
@Override
public EventExecutor next() {
return this;
}
@Override
public Promise newPromise() {
return new DefaultPromise(this);
}
@Override
public Future newSucceededFuture() {
return succeededFuture;
}
@Override
public Future newFailedFuture(Throwable cause) {
return new FailedFuture(this, cause);
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
return scheduler.schedule(this, command, delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
return scheduler.schedule(this, callable, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return scheduler.scheduleAtFixedRate(this, command, initialDelay, period, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return scheduler.scheduleWithFixedDelay(this, command, initialDelay, delay, unit);
}
}

View File

@ -0,0 +1,165 @@
/*
* 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:
*
* 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.util.concurrent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* {@link EventExecutor} which executes the command in the caller thread.
*/
public final class ImmediateEventExecutor extends AbstractEventExecutor {
public ImmediateEventExecutor(TaskScheduler scheduler) {
super(scheduler);
}
@Override
public EventExecutorGroup parent() {
return null;
}
@Override
public boolean inEventLoop() {
return true;
}
@Override
public boolean inEventLoop(Thread thread) {
return true;
}
@Override
public void shutdown() {
// NOOP
}
@Override
public boolean isShutdown() {
return false;
}
@Override
public boolean isTerminated() {
return false;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) {
return false;
}
@Override
public List<Runnable> shutdownNow() {
return Collections.emptyList();
}
@Override
public <T> Future<T> submit(Callable<T> task) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<T> future = new FutureTask<T>(task);
future.run();
return future;
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<T> future = new FutureTask<T>(task, result);
future.run();
return future;
}
@SuppressWarnings("unchecked")
@Override
public Future<?> submit(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<?> future = new FutureTask(task, null);
future.run();
return future;
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
if (tasks == null) {
throw new NullPointerException("tasks");
}
List<Future<T>> futures = new ArrayList<Future<T>>();
for (Callable<T> task: tasks) {
futures.add(submit(task));
}
return futures;
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) {
if (tasks == null) {
throw new NullPointerException("tasks");
}
List<Future<T>> futures = new ArrayList<Future<T>>();
for (Callable<T> task: tasks) {
futures.add(submit(task));
}
return futures;
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
if (tasks == null) {
throw new NullPointerException("tasks");
}
if (tasks.isEmpty()) {
throw new IllegalArgumentException("tasks must be non empty");
}
return invokeAll(tasks).get(0).get();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException {
if (tasks == null) {
throw new NullPointerException("tasks");
}
if (tasks.isEmpty()) {
throw new IllegalArgumentException("tasks must be non empty");
}
return invokeAll(tasks).get(0).get();
}
@Override
public void execute(Runnable command) {
if (command == null) {
throw new NullPointerException("command");
}
command.run();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012 The Netty Project
* 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
@ -13,25 +13,22 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
package io.netty.util.concurrent;
import java.util.concurrent.Executor;
/**
* {@link Executor} which executes the command in the caller thread.
* {@link Executor} which execute tasks in the callers thread.
*/
public final class ImmediateExecutor implements Executor {
public static final ImmediateExecutor INSTANCE = new ImmediateExecutor();
/**
* The default instance.
*/
static final ImmediateExecutor INSTANCE = new ImmediateExecutor();
private ImmediateExecutor() {
// use static instance
}
@Override
public void execute(Runnable command) {
command.run();
}
private ImmediateExecutor() {
}
}

View File

@ -24,12 +24,9 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@ -38,7 +35,7 @@ import java.util.concurrent.TimeUnit;
* Abstract base class for {@link EventExecutor}'s that execute all its submitted tasks in a single thread.
*
*/
public abstract class SingleThreadEventExecutor extends AbstractExecutorService implements EventExecutor {
public abstract class SingleThreadEventExecutor extends AbstractEventExecutor {
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(SingleThreadEventExecutor.class);
@ -72,12 +69,10 @@ public abstract class SingleThreadEventExecutor extends AbstractExecutorService
}
private final EventExecutorGroup parent;
private final Future succeededFuture = new SucceededFuture(this);
private final Queue<Runnable> taskQueue;
private final Thread thread;
private final Object stateLock = new Object();
private final Semaphore threadLock = new Semaphore(0);
private final TaskScheduler scheduler;
private final Set<Runnable> shutdownHooks = new LinkedHashSet<Runnable>();
private volatile int state = ST_NOT_STARTED;
private long lastAccessTimeNanos;
@ -92,15 +87,12 @@ public abstract class SingleThreadEventExecutor extends AbstractExecutorService
*/
protected SingleThreadEventExecutor(
EventExecutorGroup parent, ThreadFactory threadFactory, TaskScheduler scheduler) {
super(scheduler);
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
if (scheduler == null) {
throw new NullPointerException("scheduler");
}
this.parent = parent;
this.scheduler = scheduler;
thread = threadFactory.newThread(new Runnable() {
@Override
@ -166,11 +158,6 @@ public abstract class SingleThreadEventExecutor extends AbstractExecutorService
return parent;
}
@Override
public EventExecutor next() {
return this;
}
/**
* Interrupt the current running {@link Thread}.
*/
@ -479,39 +466,4 @@ public abstract class SingleThreadEventExecutor extends AbstractExecutorService
protected static void reject() {
throw new RejectedExecutionException("event executor terminated");
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
return scheduler.schedule(this, command, delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
return scheduler.schedule(this, callable, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return scheduler.scheduleAtFixedRate(this, command, initialDelay, period, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return scheduler.scheduleWithFixedDelay(this, command, initialDelay, delay, unit);
}
@Override
public Promise newPromise() {
return new DefaultPromise(this);
}
@Override
public Future newSucceededFuture() {
return succeededFuture;
}
@Override
public Future newFailedFuture(Throwable cause) {
return new FailedFuture(this, cause);
}
}

View File

@ -13,17 +13,21 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
package io.netty.util.concurrent;
import static org.junit.Assert.*;
import org.easymock.EasyMock;
import org.junit.Test;
public class ImmediateExecutorTest {
import java.util.concurrent.Executors;
public class ImmediateEventExecutorTest {
@Test
public void shouldExecuteImmediately() {
ImmediateExecutor e = ImmediateExecutor.INSTANCE;
TaskScheduler scheduler = new TaskScheduler(Executors.defaultThreadFactory());
ImmediateEventExecutor e = new ImmediateEventExecutor(scheduler);
long startTime = System.nanoTime();
e.execute(new Runnable() {
@Override
@ -42,5 +46,6 @@ public class ImmediateExecutorTest {
}
});
assertTrue(System.nanoTime() - startTime >= 1000000000L);
scheduler.shutdown();
}
}

View File

@ -30,6 +30,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.FileRegion;
import io.netty.util.concurrent.ImmediateExecutor;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

View File

@ -0,0 +1,52 @@
/*
* 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:
*
* 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.group;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
/**
* {@link ChannelException} which holds {@link ChannelFuture}s that failed because of an error.
*/
public class ChannelGroupException extends ChannelException implements Iterable<Map.Entry<Integer, Throwable>> {
private static final long serialVersionUID = -4093064295562629453L;
private final Collection<Map.Entry<Integer, Throwable>> failed;
public ChannelGroupException(Collection<Map.Entry<Integer, Throwable>> causes) {
if (causes == null) {
throw new NullPointerException("causes");
}
if (causes.isEmpty()) {
throw new IllegalArgumentException("causes must be non empty");
}
this.failed = Collections.unmodifiableCollection(causes);
}
/**
* Returns a {@link Iterator} which contains all the {@link Throwable} that was a cause of the failure and the
* related id of the {@link Channel}.
*/
@Override
public Iterator<Map.Entry<Integer, Throwable>> iterator() {
return failed.iterator();
}
}

View File

@ -19,9 +19,10 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
/**
* The result of an asynchronous {@link ChannelGroup} operation.
@ -42,13 +43,13 @@ import java.util.concurrent.TimeUnit;
* {@link ChannelGroupFutureListener} so you can get notified when the I/O
* operation have been completed.
*
* <h3>Prefer {@link #addListener(ChannelGroupFutureListener)} to {@link #await()}</h3>
* <h3>Prefer {@link #addListener(GenericFutureListener)} to {@link #await()}</h3>
*
* It is recommended to prefer {@link #addListener(ChannelGroupFutureListener)} to
* It is recommended to prefer {@link #addListener(GenericFutureListener)} to
* {@link #await()} wherever possible to get notified when I/O operations are
* done and to do any follow-up tasks.
* <p>
* {@link #addListener(ChannelGroupFutureListener)} is non-blocking. It simply
* {@link #addListener(GenericFutureListener)} is non-blocking. It simply
* adds the specified {@link ChannelGroupFutureListener} to the
* {@link ChannelGroupFuture}, and I/O thread will notify the listeners when
* the I/O operations associated with the future is done.
@ -101,7 +102,7 @@ import java.util.concurrent.TimeUnit;
* make sure you do not call {@link #await()} in an I/O thread. Otherwise,
* {@link IllegalStateException} will be raised to prevent a dead lock.
*/
public interface ChannelGroupFuture extends Iterable<ChannelFuture> {
public interface ChannelGroupFuture extends Future, Iterable<ChannelFuture> {
/**
* Returns the {@link ChannelGroup} which is associated with this future.
@ -127,18 +128,15 @@ public interface ChannelGroupFuture extends Iterable<ChannelFuture> {
*/
ChannelFuture find(Channel channel);
/**
* Returns {@code true} if and only if this future is
* complete, regardless of whether the operation was successful, failed,
* or canceled.
*/
boolean isDone();
/**
* Returns {@code true} if and only if all I/O operations associated with
* this future were successful without any failure.
*/
boolean isCompleteSuccess();
@Override
boolean isSuccess();
@Override
ChannelGroupException cause();
/**
* Returns {@code true} if and only if the I/O operations associated with
@ -146,93 +144,35 @@ public interface ChannelGroupFuture extends Iterable<ChannelFuture> {
*/
boolean isPartialSuccess();
/**
* Returns {@code true} if and only if all I/O operations associated with
* this future have failed without any success.
*/
boolean isCompleteFailure();
/**
* Returns {@code true} if and only if the I/O operations associated with
* this future have failed partially with some success.
*/
boolean isPartialFailure();
/**
* 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.
*/
ChannelGroupFuture addListener(ChannelGroupFutureListener listener);
@Override
ChannelGroupFuture addListener(GenericFutureListener<? extends Future> listener);
/**
* Removes the specified listener from this future.
* The specified listener is no longer notified when this
* future is {@linkplain #isDone() done}. If this
* future is already completed, this method has no effect
* and returns silently.
*/
ChannelGroupFuture removeListener(ChannelGroupFutureListener listener);
@Override
ChannelGroupFuture addListeners(GenericFutureListener<? extends Future>... listeners);
/**
* Waits for this future to be completed.
*
* @throws InterruptedException
* if the current thread was interrupted
*/
@Override
ChannelGroupFuture removeListener(GenericFutureListener<? extends Future> listener);
@Override
ChannelGroupFuture removeListeners(GenericFutureListener<? extends Future>... listeners);
@Override
ChannelGroupFuture await() throws InterruptedException;
/**
* Waits for this future to be completed without
* interruption. This method catches an {@link InterruptedException} and
* discards it silently.
*/
@Override
ChannelGroupFuture 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;
@Override
ChannelGroupFuture syncUninterruptibly();
/**
* 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);
@Override
ChannelGroupFuture sync() throws InterruptedException;
/**
* Returns the {@link Iterator} that enumerates all {@link ChannelFuture}s

View File

@ -15,22 +15,14 @@
*/
package io.netty.channel.group;
import java.util.EventListener;
import io.netty.util.concurrent.GenericFutureListener;
/**
* Listens to the result of a {@link ChannelGroupFuture}. The result of the
* asynchronous {@link ChannelGroup} I/O operations is notified once this
* listener is added by calling {@link ChannelGroupFuture#addListener(ChannelGroupFutureListener)}
* listener is added by calling {@link ChannelGroupFuture#addListener(GenericFutureListener)}
* and all I/O operations are complete.
*/
public interface ChannelGroupFutureListener extends EventListener {
public interface ChannelGroupFutureListener extends GenericFutureListener<ChannelGroupFuture> {
/**
* Invoked when all I/O operations associated with the
* {@link ChannelGroupFuture} have been completed.
*
* @param future The source {@link ChannelGroupFuture} which called this
* callback.
*/
void operationComplete(ChannelGroupFuture future) throws Exception;
}

View File

@ -21,15 +21,31 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.FileRegion;
import io.netty.channel.ServerChannel;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.FailedFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.SucceededFuture;
import io.netty.util.internal.PlatformDependent;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@ -38,8 +54,9 @@ import java.util.concurrent.atomic.AtomicInteger;
public class DefaultChannelGroup extends AbstractSet<Channel> implements ChannelGroup {
private static final AtomicInteger nextId = new AtomicInteger();
private static final ImmediateEventExecutor DEFAULT_EXECUTOR = new ImmediateEventExecutor();
private final String name;
private final EventExecutor executor;
private final ConcurrentMap<Integer, Channel> serverChannels = PlatformDependent.newConcurrentHashMap();
private final ConcurrentMap<Integer, Channel> nonServerChannels = PlatformDependent.newConcurrentHashMap();
private final ChannelFutureListener remover = new ChannelFutureListener() {
@ -56,16 +73,34 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
this("group-0x" + Integer.toHexString(nextId.incrementAndGet()));
}
/**
* Creates a new group with a generated name amd the provided {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s.
*/
public DefaultChannelGroup(EventExecutor executor) {
this("group-0x" + Integer.toHexString(nextId.incrementAndGet()), executor);
}
/**
* Creates a new group with the specified {@code name}. Please note that
* different groups can have the same name, which means no duplicate check
* is done against group names.
*/
public DefaultChannelGroup(String name) {
this(name, DEFAULT_EXECUTOR);
}
/**
* Creates a new group with the specified {@code name} and {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s. Please note that different groups can have the same name, which means no
* duplicate check is done against group names.
*/
public DefaultChannelGroup(String name, EventExecutor executor) {
if (name == null) {
throw new NullPointerException("name");
}
this.name = name;
this.executor = executor;
}
@Override
@ -187,7 +222,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
futures.put(c.id(), c.close());
}
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -202,7 +237,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
futures.put(c.id(), c.disconnect());
}
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -218,7 +253,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
}
BufUtil.release(message);
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -234,7 +269,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
}
BufUtil.release(region);
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -244,7 +279,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
futures.put(c.id(), c.flush());
}
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -259,7 +294,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
futures.put(c.id(), c.deregister());
}
return new DefaultChannelGroupFuture(this, futures);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
@ -287,4 +322,177 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
return getClass().getSimpleName() +
"(name: " + name() + ", size: " + size() + ')';
}
private static final class ImmediateEventExecutor implements EventExecutor {
private final Future successedFuture = new SucceededFuture(this);
@Override
public EventExecutor next() {
return this;
}
@Override
public EventExecutorGroup parent() {
return null;
}
@Override
public boolean inEventLoop() {
return true;
}
@Override
public boolean inEventLoop(Thread thread) {
return true;
}
@Override
public void shutdown() {
}
@Override
public boolean isShutdown() {
return false;
}
@Override
public boolean isTerminated() {
return false;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Promise newPromise() {
return new DefaultPromise(this);
}
@Override
public Future newSucceededFuture() {
return successedFuture;
}
@Override
public Future newFailedFuture(Throwable cause) {
return new FailedFuture(this, cause);
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
long period, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
long delay, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public List<Runnable> shutdownNow() {
return Collections.emptyList();
}
@Override
public <T> java.util.concurrent.Future<T> submit(Callable<T> task) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<T> future = new FutureTask<T>(task);
future.run();
return future;
}
@Override
public <T> java.util.concurrent.Future<T> submit(Runnable task, T result) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<T> future = new FutureTask<T>(task, result);
future.run();
return future;
}
@SuppressWarnings("unchecked")
@Override
public java.util.concurrent.Future<?> submit(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
FutureTask<?> future = new FutureTask(task, null);
future.run();
return future;
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
if (tasks == null) {
throw new NullPointerException("tasks");
}
List<java.util.concurrent.Future<T>> futures = new ArrayList<java.util.concurrent.Future<T>>();
for (Callable<T> task: tasks) {
futures.add(submit(task));
}
return futures;
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) {
if (tasks == null) {
throw new NullPointerException("tasks");
}
List<java.util.concurrent.Future<T>> futures = new ArrayList<java.util.concurrent.Future<T>>();
for (Callable<T> task: tasks) {
futures.add(submit(task));
}
return futures;
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
if (tasks == null) {
throw new NullPointerException("tasks");
}
if (tasks.isEmpty()) {
throw new IllegalArgumentException("tasks must be non empty");
}
return invokeAll(tasks).get(0).get();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException {
if (tasks == null) {
throw new NullPointerException("tasks");
}
if (tasks.isEmpty()) {
throw new IllegalArgumentException("tasks must be non empty");
}
return invokeAll(tasks).get(0).get();
}
@Override
public void execute(Runnable command) {
if (command == null) {
throw new NullPointerException("command");
}
command.run();
}
}
}

View File

@ -18,8 +18,11 @@ package io.netty.channel.group;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
import java.util.ArrayList;
import java.util.Collection;
@ -28,26 +31,17 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*;
/**
* The default {@link ChannelGroupFuture} implementation.
*/
public class DefaultChannelGroupFuture implements ChannelGroupFuture {
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(DefaultChannelGroupFuture.class);
final class DefaultChannelGroupFuture extends DefaultPromise implements ChannelGroupFuture {
private final ChannelGroup group;
final Map<Integer, ChannelFuture> futures;
private ChannelGroupFutureListener firstListener;
private List<ChannelGroupFutureListener> otherListeners;
private boolean done;
int successCount;
int failureCount;
private int waiters;
private final Map<Integer, ChannelFuture> futures;
private int successCount;
private int failureCount;
private final ChannelFutureListener childListener = new ChannelFutureListener() {
@Override
@ -66,7 +60,18 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
}
if (callSetDone) {
setDone();
if (failureCount > 0) {
List<Map.Entry<Integer, Throwable>> failed =
new ArrayList<Map.Entry<Integer, Throwable>>(failureCount);
for (ChannelFuture f: futures.values()) {
if (!f.isSuccess()) {
failed.add(new DefaultEntry<Integer, Throwable>(f.channel().id(), f.cause()));
}
}
setFailure0(new ChannelGroupException(failed));
} else {
setSuccess0();
}
}
}
};
@ -74,7 +79,8 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
/**
* Creates a new instance.
*/
public DefaultChannelGroupFuture(ChannelGroup group, Collection<ChannelFuture> futures) {
public DefaultChannelGroupFuture(ChannelGroup group, Collection<ChannelFuture> futures, EventExecutor executor) {
super(executor);
if (group == null) {
throw new NullPointerException("group");
}
@ -97,11 +103,12 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
// Done on arrival?
if (this.futures.isEmpty()) {
setDone();
setSuccess0();
}
}
DefaultChannelGroupFuture(ChannelGroup group, Map<Integer, ChannelFuture> futures) {
DefaultChannelGroupFuture(ChannelGroup group, Map<Integer, ChannelFuture> futures, EventExecutor executor) {
super(executor);
this.group = group;
this.futures = Collections.unmodifiableMap(futures);
for (ChannelFuture f: this.futures.values()) {
@ -110,7 +117,7 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
// Done on arrival?
if (this.futures.isEmpty()) {
setDone();
setSuccess0();
}
}
@ -134,16 +141,6 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
return futures.values().iterator();
}
@Override
public synchronized boolean isDone() {
return done;
}
@Override
public synchronized boolean isCompleteSuccess() {
return successCount == futures.size();
}
@Override
public synchronized boolean isPartialSuccess() {
return successCount != 0 && successCount != futures.size();
@ -155,224 +152,108 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture {
}
@Override
public synchronized boolean isCompleteFailure() {
int futureCnt = futures.size();
return futureCnt != 0 && failureCount == futureCnt;
}
@Override
public ChannelGroupFuture addListener(ChannelGroupFutureListener listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
boolean notifyNow = false;
synchronized (this) {
if (done) {
notifyNow = true;
} else {
if (firstListener == null) {
firstListener = listener;
} else {
if (otherListeners == null) {
otherListeners = new ArrayList<ChannelGroupFutureListener>(1);
}
otherListeners.add(listener);
}
}
}
if (notifyNow) {
notifyListener(listener);
}
public DefaultChannelGroupFuture addListener(GenericFutureListener<? extends Future> listener) {
super.addListener(listener);
return this;
}
@Override
public ChannelGroupFuture removeListener(ChannelGroupFutureListener listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
synchronized (this) {
if (!done) {
if (listener == firstListener) {
if (otherListeners != null && !otherListeners.isEmpty()) {
firstListener = otherListeners.remove(0);
} else {
firstListener = null;
}
} else if (otherListeners != null) {
otherListeners.remove(listener);
}
}
}
public DefaultChannelGroupFuture addListeners(GenericFutureListener<? extends Future>... listeners) {
super.addListeners(listeners);
return this;
}
@Override
public ChannelGroupFuture await() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
synchronized (this) {
while (!done) {
waiters++;
try {
wait();
} finally {
waiters--;
}
}
}
public DefaultChannelGroupFuture removeListener(GenericFutureListener<? extends Future> listener) {
super.removeListener(listener);
return this;
}
@Override
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return await0(unit.toNanos(timeout), true);
}
@Override
public boolean await(long timeoutMillis) throws InterruptedException {
return await0(MILLISECONDS.toNanos(timeoutMillis), true);
}
@Override
public ChannelGroupFuture awaitUninterruptibly() {
boolean interrupted = false;
synchronized (this) {
while (!done) {
waiters++;
try {
wait();
} catch (InterruptedException e) {
interrupted = true;
} finally {
waiters--;
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
public DefaultChannelGroupFuture removeListeners(GenericFutureListener<? extends Future>... listeners) {
super.removeListeners(listeners);
return this;
}
@Override
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
try {
return await0(unit.toNanos(timeout), false);
} catch (InterruptedException e) {
throw new InternalError();
}
public DefaultChannelGroupFuture await() throws InterruptedException {
super.await();
return this;
}
@Override
public boolean awaitUninterruptibly(long timeoutMillis) {
try {
return await0(MILLISECONDS.toNanos(timeoutMillis), false);
} catch (InterruptedException e) {
throw new InternalError();
}
public DefaultChannelGroupFuture awaitUninterruptibly() {
super.awaitUninterruptibly();
return this;
}
private boolean await0(long timeoutNanos, boolean interruptable) throws InterruptedException {
if (interruptable && Thread.interrupted()) {
throw new InterruptedException();
}
long startTime = timeoutNanos <= 0 ? 0 : System.nanoTime();
long waitTime = timeoutNanos;
boolean interrupted = false;
try {
synchronized (this) {
if (done || waitTime <= 0) {
return done;
}
waiters++;
try {
for (;;) {
try {
wait(waitTime / 1000000, (int) (waitTime % 1000000));
} catch (InterruptedException e) {
if (interruptable) {
throw e;
} else {
interrupted = true;
}
}
if (done) {
return true;
} else {
waitTime = timeoutNanos - (System.nanoTime() - startTime);
if (waitTime <= 0) {
return done;
}
}
}
} finally {
waiters--;
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
@Override
public DefaultChannelGroupFuture syncUninterruptibly() {
super.syncUninterruptibly();
return this;
}
void setDone() {
synchronized (this) {
// Allow only once.
if (done) {
return;
}
done = true;
if (waiters > 0) {
notifyAll();
}
}
notifyListeners();
@Override
public DefaultChannelGroupFuture sync() throws InterruptedException {
super.sync();
return this;
}
private void notifyListeners() {
// This method doesn't need synchronization because:
// 1) This method is always called after synchronized (this) block.
// Hence any listener list modification happens-before this method.
// 2) This method is called only when 'done' is true. Once 'done'
// becomes true, the listener list is never modified - see add/removeListener()
if (firstListener != null) {
notifyListener(firstListener);
firstListener = null;
if (otherListeners != null) {
for (ChannelGroupFutureListener l: otherListeners) {
notifyListener(l);
}
otherListeners = null;
}
}
@Override
public ChannelGroupException cause() {
return (ChannelGroupException) super.cause();
}
private void notifyListener(ChannelGroupFutureListener l) {
try {
l.operationComplete(this);
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn(
"An exception was thrown by " +
ChannelFutureListener.class.getSimpleName() + '.', t);
}
private void setSuccess0() {
super.setSuccess();
}
private void setFailure0(ChannelGroupException cause) {
super.setFailure(cause);
}
@Override
public Promise setSuccess() {
throw new IllegalStateException();
}
@Override
public boolean trySuccess() {
throw new IllegalStateException();
}
@Override
public Promise setFailure(Throwable cause) {
throw new IllegalStateException();
}
@Override
public boolean tryFailure(Throwable cause) {
throw new IllegalStateException();
}
private static final class DefaultEntry<K, V> implements Map.Entry<K, V> {
private final K key;
private final V value;
public DefaultEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException("read-only");
}
}
}