Fix IllegalStateException triggered by a successful connection attempt

- Fixes #1467
- Provide more information about the tasks and promises on exception for easier debugging
This commit is contained in:
Trustin Lee 2013-06-19 11:45:31 +09:00
parent 8570c717ad
commit 6f86f38ae9
3 changed files with 96 additions and 14 deletions

View File

@ -17,6 +17,7 @@ package io.netty.util.concurrent;
import io.netty.util.Signal; import io.netty.util.Signal;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -228,7 +229,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
} }
if (Thread.interrupted()) { if (Thread.interrupted()) {
throw new InterruptedException(); throw new InterruptedException(toString());
} }
synchronized (this) { synchronized (this) {
@ -312,7 +313,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
} }
if (interruptable && Thread.interrupted()) { if (interruptable && Thread.interrupted()) {
throw new InterruptedException(); throw new InterruptedException(toString());
} }
long startTime = timeoutNanos <= 0 ? 0 : System.nanoTime(); long startTime = timeoutNanos <= 0 ? 0 : System.nanoTime();
@ -369,7 +370,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
protected void checkDeadLock() { protected void checkDeadLock() {
EventExecutor e = executor(); EventExecutor e = executor();
if (e != null && e.inEventLoop()) { if (e != null && e.inEventLoop()) {
throw new BlockingOperationException(); throw new BlockingOperationException(toString());
} }
} }
@ -379,7 +380,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
notifyListeners(); notifyListeners();
return this; return this;
} }
throw new IllegalStateException("complete already"); throw new IllegalStateException("complete already: " + this);
} }
@Override @Override
@ -397,7 +398,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
notifyListeners(); notifyListeners();
return this; return this;
} }
throw new IllegalStateException("complete already", cause); throw new IllegalStateException("complete already: " + this, cause);
} }
@Override @Override
@ -437,14 +438,14 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
public boolean setUncancellable() { public boolean setUncancellable() {
Object result = this.result; Object result = this.result;
if (isDone0(result)) { if (isDone0(result)) {
return isCancelled0(result); return false;
} }
synchronized (this) { synchronized (this) {
// Allow only once. // Allow only once.
result = this.result; result = this.result;
if (isDone0(result)) { if (isDone0(result)) {
return isCancelled0(result); return false;
} }
this.result = UNCANCELLABLE; this.result = UNCANCELLABLE;
@ -509,7 +510,7 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
private void incWaiters() { private void incWaiters() {
if (waiters == Short.MAX_VALUE) { if (waiters == Short.MAX_VALUE) {
throw new IllegalStateException("too many waiters"); throw new IllegalStateException("too many waiters: " + this);
} }
waiters ++; waiters ++;
} }
@ -734,4 +735,30 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
this.cause = cause; this.cause = cause;
} }
} }
@Override
public String toString() {
return toStringBuilder().toString();
}
protected StringBuilder toStringBuilder() {
StringBuilder buf = new StringBuilder(64);
buf.append(StringUtil.simpleClassName(this));
buf.append('@');
buf.append(Integer.toHexString(hashCode()));
Object result = this.result;
if (result == SUCCESS) {
buf.append("(success)");
} else if (result == UNCANCELLABLE) {
buf.append("(uncancellable)");
} else if (result instanceof CauseHolder) {
buf.append("(failure(");
buf.append(((CauseHolder) result).cause);
buf.append(')');
} else {
buf.append("(incomplete)");
}
return buf;
}
} }

View File

@ -16,15 +16,41 @@
package io.netty.util.concurrent; package io.netty.util.concurrent;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.RunnableFuture; import java.util.concurrent.RunnableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> { class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
static <T> Callable<T> toCallable(Runnable runnable, T result) {
return new RunnableAdapter<T>(runnable, result);
}
private static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
@Override
public T call() {
task.run();
return result;
}
@Override
public String toString() {
return "Callable(task: " + task + ", result: " + result + ')';
}
}
protected final Callable<V> task; protected final Callable<V> task;
PromiseTask(EventExecutor executor, Runnable runnable, V result) { PromiseTask(EventExecutor executor, Runnable runnable, V result) {
this(executor, Executors.callable(runnable, result)); this(executor, toCallable(runnable, result));
} }
PromiseTask(EventExecutor executor, Callable<V> callable) { PromiseTask(EventExecutor executor, Callable<V> callable) {
@ -45,8 +71,10 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
@Override @Override
public void run() { public void run() {
try { try {
V result = task.call(); if (setUncancellableInternal()) {
setSuccessInternal(result); V result = task.call();
setSuccessInternal(result);
}
} catch (Throwable e) { } catch (Throwable e) {
setFailureInternal(e); setFailureInternal(e);
} }
@ -58,6 +86,8 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
} }
protected final Promise<V> setFailureInternal(Throwable cause) { protected final Promise<V> setFailureInternal(Throwable cause) {
Logger.getLogger(PromiseTask.class.getName()).log(
Level.WARNING, "Calling setFailureInternal(" + cause + ") on " + this, new Exception());
super.setFailure(cause); super.setFailure(cause);
return this; return this;
} }
@ -77,6 +107,8 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
} }
protected final Promise<V> setSuccessInternal(V result) { protected final Promise<V> setSuccessInternal(V result) {
Logger.getLogger(PromiseTask.class.getName()).log(
Level.WARNING, "Calling setSuccessInternal() on " + this, new Exception());
super.setSuccess(result); super.setSuccess(result);
return this; return this;
} }
@ -98,4 +130,14 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
protected final boolean setUncancellableInternal() { protected final boolean setUncancellableInternal() {
return super.setUncancellable(); return super.setUncancellable();
} }
@Override
protected StringBuilder toStringBuilder() {
StringBuilder buf = super.toStringBuilder();
buf.setCharAt(buf.length() - 1, ',');
buf.append(" task: ");
buf.append(task);
buf.append(')');
return buf;
}
} }

View File

@ -19,7 +19,6 @@ package io.netty.util.concurrent;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -46,7 +45,7 @@ final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFu
EventExecutor executor, Queue<ScheduledFutureTask<?>> delayedTaskQueue, EventExecutor executor, Queue<ScheduledFutureTask<?>> delayedTaskQueue,
Runnable runnable, V result, long nanoTime) { Runnable runnable, V result, long nanoTime) {
this(executor, delayedTaskQueue, Executors.callable(runnable, result), nanoTime); this(executor, delayedTaskQueue, toCallable(runnable, result), nanoTime);
} }
ScheduledFutureTask( ScheduledFutureTask(
@ -145,4 +144,18 @@ final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFu
setFailureInternal(cause); setFailureInternal(cause);
} }
} }
@Override
protected StringBuilder toStringBuilder() {
StringBuilder buf = super.toStringBuilder();
buf.setCharAt(buf.length() - 1, ',');
buf.append(" id: ");
buf.append(id);
buf.append(", deadline: ");
buf.append(deadlineNanos);
buf.append(", period: ");
buf.append(periodNanos);
buf.append(')');
return buf;
}
} }