Create a stackless ClosedChannelException to reduce overhead when the… (#10523)

Motivation:

In some benchmarks closing the Channel attributes to a lot of overhead due the call of fillInStackTrace(). We should reduce this overhead.

Modifications:

- Create a StacklessClosedChannelException and use it to reduce overhead.
- Only call ChannelOutboundBuffer.failFlushed(...) when there was a flushed message at all.

Result:

Less performance overhead when closing the Channel
This commit is contained in:
Norman Maurer 2020-09-01 15:25:41 +02:00
parent a49afaef35
commit 07632a5a4c
2 changed files with 59 additions and 12 deletions

View File

@ -588,7 +588,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
public final void close(final ChannelPromise promise) {
assertEventLoop();
ClosedChannelException closedChannelException = new ClosedChannelException();
ClosedChannelException closedChannelException =
StacklessClosedChannelException.newInstance(AbstractChannel.class, "close(ChannelPromise)");
close(promise, closedChannelException, closedChannelException, false);
}
@ -827,7 +828,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
// need to fail the future right away. If it is not null the handling of the rest
// will be done in flush0()
// See https://github.com/netty/netty/issues/2362
safeSetFailure(promise, newClosedChannelException(initialCloseCause));
safeSetFailure(promise, newClosedChannelException(initialCloseCause, "write(Object, ChannelPromise)"));
// release message now to prevent resource-leak
ReferenceCountUtil.release(msg);
return;
@ -882,11 +883,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
// Mark all pending write requests as failure if the channel is inactive.
if (!isActive()) {
try {
if (isOpen()) {
outboundBuffer.failFlushed(new NotYetConnectedException(), true);
} else {
// Do not trigger channelWritabilityChanged because the channel is closed already.
outboundBuffer.failFlushed(newClosedChannelException(initialCloseCause), false);
// Check if we need to generate the exception at all.
if (!outboundBuffer.isEmpty()) {
if (isOpen()) {
outboundBuffer.failFlushed(new NotYetConnectedException(), true);
} else {
// Do not trigger channelWritabilityChanged because the channel is closed already.
outboundBuffer.failFlushed(newClosedChannelException(initialCloseCause, "flush0()"), false);
}
}
} finally {
inFlush0 = false;
@ -907,13 +911,13 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
* may still return {@code true} even if the channel should be closed as result of the exception.
*/
initialCloseCause = t;
close(voidPromise(), t, newClosedChannelException(t), false);
close(voidPromise(), t, newClosedChannelException(t, "flush0()"), false);
} else {
try {
shutdownOutput(voidPromise(), t);
} catch (Throwable t2) {
initialCloseCause = t;
close(voidPromise(), t2, newClosedChannelException(t), false);
close(voidPromise(), t2, newClosedChannelException(t, "flush0()"), false);
}
}
} finally {
@ -921,8 +925,9 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
}
}
private ClosedChannelException newClosedChannelException(Throwable cause) {
ClosedChannelException exception = new ClosedChannelException();
private ClosedChannelException newClosedChannelException(Throwable cause, String method) {
ClosedChannelException exception =
StacklessClosedChannelException.newInstance(AbstractChannel.AbstractUnsafe.class, method);
if (cause != null) {
exception.initCause(cause);
}
@ -941,7 +946,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
return true;
}
safeSetFailure(promise, newClosedChannelException(initialCloseCause));
safeSetFailure(promise, newClosedChannelException(initialCloseCause, "ensureOpen(ChannelPromise)"));
return false;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel;
import io.netty.util.internal.ThrowableUtil;
import java.nio.channels.ClosedChannelException;
/**
* Cheap {@link ClosedChannelException} that does not fill in the stacktrace.
*/
final class StacklessClosedChannelException extends ClosedChannelException {
private static final long serialVersionUID = -2214806025529435136L;
private StacklessClosedChannelException() { }
@Override
public Throwable fillInStackTrace() {
return this;
}
/**
* Creates a new {@link StacklessClosedChannelException} which has the origin of the given {@link Class} and method.
*/
static StacklessClosedChannelException newInstance(Class<?> clazz, String method) {
return ThrowableUtil.unknownStackTrace(new StacklessClosedChannelException(), clazz, method);
}
}