Skip to content

Commit b31e7f2

Browse files
authored
Fix scalability issue due to checkcast on context's invoke operations (#12806)
Motivation: ChannelDuplexHandler can implements both ChannelOutboundHandler and ChannelInboundHandler causing a scalability issue due to checkcast due to https://bugs.openjdk.org/browse/JDK-8180450 Modifications: Peeling-off invoke methods turning the checkcast vs interfaces into an instanceof vs ChannelDuplexHandler, saving the scalability issue to happen. Sadly, if users manually implements both ChannelOutboundHandler and ChannelInboundHandler without extending ChannelDuplexHandler the fix won't be enough. Result: Scalable duplex channel handler operations.
1 parent 5327fae commit b31e7f2

File tree

3 files changed

+315
-18
lines changed

3 files changed

+315
-18
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2022 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.netty.microbench.channel;
17+
18+
import io.netty.channel.ChannelDuplexHandler;
19+
import io.netty.channel.ChannelHandlerContext;
20+
import io.netty.channel.ChannelInboundHandlerAdapter;
21+
import io.netty.channel.ChannelOutboundHandlerAdapter;
22+
import io.netty.channel.ChannelPipeline;
23+
import io.netty.channel.embedded.EmbeddedChannel;
24+
import io.netty.microbench.util.AbstractMicrobenchmark;
25+
import org.openjdk.jmh.annotations.Benchmark;
26+
import org.openjdk.jmh.annotations.Measurement;
27+
import org.openjdk.jmh.annotations.Param;
28+
import org.openjdk.jmh.annotations.Scope;
29+
import org.openjdk.jmh.annotations.Setup;
30+
import org.openjdk.jmh.annotations.State;
31+
import org.openjdk.jmh.annotations.TearDown;
32+
import org.openjdk.jmh.annotations.Threads;
33+
import org.openjdk.jmh.annotations.Warmup;
34+
import org.openjdk.jmh.infra.Blackhole;
35+
36+
@Warmup(iterations = 5, time = 1)
37+
@Measurement(iterations = 5, time = 1)
38+
@State(Scope.Thread)
39+
public class DefaultChannelPipelineDuplexHandlerBenchmark extends AbstractMicrobenchmark {
40+
41+
private ChannelPipeline pipeline;
42+
private EmbeddedChannel channel;
43+
44+
@Param({"true", "false"})
45+
private boolean duplex;
46+
47+
@Setup
48+
public void setup() {
49+
channel = new EmbeddedChannel() {
50+
@Override
51+
public void runPendingTasks() {
52+
// NO-OP to reduce noise on flush
53+
}
54+
};
55+
// disabling auto-read to reduce noise on flush
56+
channel.config().setAutoRead(false);
57+
pipeline = channel.pipeline();
58+
pipeline.addLast(new ChannelInboundHandlerAdapter() {
59+
@Override
60+
public void channelReadComplete(final ChannelHandlerContext ctx) {
61+
ctx.fireChannelReadComplete();
62+
}
63+
});
64+
// Duplex handlers implements both out/in interfaces causing a scalability issue
65+
// see https://bugs.openjdk.org/browse/JDK-8180450
66+
if (duplex) {
67+
pipeline.addLast(new ChannelDuplexHandler() {
68+
@Override
69+
public void channelReadComplete(final ChannelHandlerContext ctx) {
70+
ctx.fireChannelReadComplete();
71+
}
72+
73+
@Override
74+
public void flush(final ChannelHandlerContext ctx) {
75+
ctx.flush();
76+
}
77+
});
78+
pipeline.addLast(new ChannelDuplexHandler() {
79+
@Override
80+
public void channelReadComplete(final ChannelHandlerContext ctx) {
81+
ctx.flush();
82+
}
83+
});
84+
} else {
85+
pipeline.addLast(new ChannelInboundHandlerAdapter() {
86+
@Override
87+
public void channelReadComplete(final ChannelHandlerContext ctx) {
88+
ctx.fireChannelReadComplete();
89+
}
90+
});
91+
pipeline.addLast(new ChannelOutboundHandlerAdapter() {
92+
@Override
93+
public void flush(final ChannelHandlerContext ctx) {
94+
ctx.flush();
95+
}
96+
});
97+
pipeline.addLast(new ChannelInboundHandlerAdapter() {
98+
@Override
99+
public void channelReadComplete(final ChannelHandlerContext ctx) {
100+
ctx.flush();
101+
}
102+
});
103+
}
104+
}
105+
106+
@TearDown
107+
public void tearDown() {
108+
pipeline.channel().close();
109+
}
110+
111+
@Benchmark
112+
public void propagateEvent(Blackhole hole) {
113+
hole.consume(pipeline.fireChannelReadComplete());
114+
}
115+
116+
@Benchmark
117+
@Threads(4)
118+
public void parallelPropagateEvent(Blackhole hole) {
119+
hole.consume(pipeline.fireChannelReadComplete());
120+
}
121+
}

0 commit comments

Comments
 (0)