Make sure ChannelFutureListeners are invoked from an event loop thread

This commit is contained in:
Trustin Lee 2012-05-01 18:31:17 +09:00
parent 0682421ce1
commit a83b9704fa
2 changed files with 55 additions and 11 deletions

View File

@ -15,11 +15,11 @@
*/ */
package io.netty.channel; package io.netty.channel;
import java.util.concurrent.TimeUnit;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory; import io.netty.logging.InternalLoggerFactory;
import java.util.concurrent.TimeUnit;
/** /**
* A skeletal {@link ChannelFuture} implementation which represents a * A skeletal {@link ChannelFuture} implementation which represents a
* {@link ChannelFuture} which has been completed already. * {@link ChannelFuture} which has been completed already.
@ -44,7 +44,20 @@ public abstract class CompleteChannelFuture implements ChannelFuture {
} }
@Override @Override
public void addListener(ChannelFutureListener listener) { public void addListener(final ChannelFutureListener listener) {
if (channel().eventLoop().inEventLoop()) {
notifyListener(listener);
} else {
channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
notifyListener(listener);
}
});
}
}
private void notifyListener(ChannelFutureListener listener) {
try { try {
listener.operationComplete(this); listener.operationComplete(this);
} catch (Throwable t) { } catch (Throwable t) {

View File

@ -16,16 +16,16 @@
package io.netty.channel; package io.netty.channel;
import static java.util.concurrent.TimeUnit.*; import static java.util.concurrent.TimeUnit.*;
import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory;
import io.netty.util.internal.DeadLockProofWorker;
import java.nio.channels.Channels;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory;
import io.netty.util.internal.DeadLockProofWorker;
/** /**
* The default {@link ChannelFuture} implementation. It is recommended to * The default {@link ChannelFuture} implementation. It is recommended to
* use {@link Channels#future(Channel)} and {@link Channels#future(Channel, boolean)} * use {@link Channels#future(Channel)} and {@link Channels#future(Channel, boolean)}
@ -119,7 +119,7 @@ public class DefaultChannelFuture implements ChannelFuture {
} }
@Override @Override
public void addListener(ChannelFutureListener listener) { public void addListener(final ChannelFutureListener listener) {
if (listener == null) { if (listener == null) {
throw new NullPointerException("listener"); throw new NullPointerException("listener");
} }
@ -148,7 +148,17 @@ public class DefaultChannelFuture implements ChannelFuture {
} }
if (notifyNow) { if (notifyNow) {
notifyListener(listener); if (channel().eventLoop().inEventLoop()) {
notifyListener(listener);
} else {
channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
notifyListener(listener);
}
});
}
} }
} }
@ -182,7 +192,7 @@ public class DefaultChannelFuture implements ChannelFuture {
if (!isDone()) { if (!isDone()) {
return this; return this;
} }
Throwable cause = cause(); Throwable cause = cause();
if (cause == null) { if (cause == null) {
return this; return this;
@ -398,7 +408,12 @@ public class DefaultChannelFuture implements ChannelFuture {
// Hence any listener list modification happens-before this method. // Hence any listener list modification happens-before this method.
// 2) This method is called only when 'done' is true. Once 'done' // 2) This method is called only when 'done' is true. Once 'done'
// becomes true, the listener list is never modified - see add/removeListener() // becomes true, the listener list is never modified - see add/removeListener()
if (firstListener != null) {
if (firstListener == null) {
return;
}
if (channel().eventLoop().inEventLoop()) {
notifyListener(firstListener); notifyListener(firstListener);
firstListener = null; firstListener = null;
@ -408,6 +423,22 @@ public class DefaultChannelFuture implements ChannelFuture {
} }
otherListeners = null; otherListeners = null;
} }
} else {
final ChannelFutureListener firstListener = this.firstListener;
final List<ChannelFutureListener> otherListeners = this.otherListeners;
this.firstListener = null;
this.otherListeners = null;
channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
notifyListener(firstListener);
if (otherListeners != null) {
for (ChannelFutureListener l: otherListeners) {
notifyListener(l);
}
}
}
});
} }
} }