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:
parent
8570c717ad
commit
6f86f38ae9
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user