DefaultPromise StackOverFlowException

Motivation:
When the ImmediateEventExecutor is in use it is possible to get a StackOverFlowException if when a promise completes a new listener is added to that promise.

Modifications:
- Protect against the case where LateListeners.run() smashes the stack.

Result:
Fixes https://github.com/netty/netty/issues/4395
This commit is contained in:
Scott Mitchell 2015-10-26 16:59:01 -07:00
parent d66520db1b
commit ed98cd8200
2 changed files with 35 additions and 2 deletions

View File

@ -836,7 +836,8 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
@Override
public void run() {
if (listeners == null) {
final EventExecutor executor = executor();
if (listeners == null || executor == ImmediateEventExecutor.INSTANCE) {
for (;;) {
GenericFutureListener<?> l = poll();
if (l == null) {
@ -847,7 +848,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
} else {
// Reschedule until the initial notification is done to avoid the race condition
// where the notification is made in an incorrect order.
execute(executor(), this);
execute(executor, this);
}
}
}

View File

@ -143,6 +143,38 @@ public class DefaultPromiseTest {
testListenerNotifyLater(2);
}
@Test(timeout = 2000)
public void testPromiseListenerAddWhenCompleteFailure() throws Exception {
testPromiseListenerAddWhenComplete(new RuntimeException());
}
@Test(timeout = 2000)
public void testPromiseListenerAddWhenCompleteSuccess() throws Exception {
testPromiseListenerAddWhenComplete(null);
}
private static void testPromiseListenerAddWhenComplete(Throwable cause) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final Promise<Void> promise = new DefaultPromise<Void>(ImmediateEventExecutor.INSTANCE);
promise.addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
promise.addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
latch.countDown();
}
});
}
});
if (cause == null) {
promise.setSuccess(null);
} else {
promise.setFailure(cause);
}
latch.await();
}
private static void testListenerNotifyLater(final int numListenersBefore) throws Exception {
EventExecutor executor = new TestEventExecutor();
int expectedCount = numListenersBefore + 2;