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:
parent
d66520db1b
commit
ed98cd8200
@ -836,7 +836,8 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (listeners == null) {
|
final EventExecutor executor = executor();
|
||||||
|
if (listeners == null || executor == ImmediateEventExecutor.INSTANCE) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
GenericFutureListener<?> l = poll();
|
GenericFutureListener<?> l = poll();
|
||||||
if (l == null) {
|
if (l == null) {
|
||||||
@ -847,7 +848,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
|
|||||||
} else {
|
} else {
|
||||||
// Reschedule until the initial notification is done to avoid the race condition
|
// Reschedule until the initial notification is done to avoid the race condition
|
||||||
// where the notification is made in an incorrect order.
|
// where the notification is made in an incorrect order.
|
||||||
execute(executor(), this);
|
execute(executor, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,38 @@ public class DefaultPromiseTest {
|
|||||||
testListenerNotifyLater(2);
|
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 {
|
private static void testListenerNotifyLater(final int numListenersBefore) throws Exception {
|
||||||
EventExecutor executor = new TestEventExecutor();
|
EventExecutor executor = new TestEventExecutor();
|
||||||
int expectedCount = numListenersBefore + 2;
|
int expectedCount = numListenersBefore + 2;
|
||||||
|
Loading…
Reference in New Issue
Block a user