Replace synchronized with ConcurrentHashMap in Http2StreamChannelBootstrap (#9848)

Motivation:

97361fa2c8 replace synchronized with ConcurrentHashMap in *Bootstrap classes but missed to do the same for the Http2 variant.

Modifications:

- Use ConcurrentHashMap
- Simplify code in *Bootstrap classes

Result:

Less contention
This commit is contained in:
Norman Maurer 2019-12-06 10:59:55 +01:00
parent 4a5712f8d9
commit 254732b43c
4 changed files with 37 additions and 45 deletions

View File

@ -34,15 +34,19 @@ import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@UnstableApi @UnstableApi
public final class Http2StreamChannelBootstrap { public final class Http2StreamChannelBootstrap {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class);
@SuppressWarnings("unchecked")
private static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
@SuppressWarnings("unchecked")
private static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<>(); private final Map<ChannelOption<?>, Object> options = new ConcurrentHashMap<>();
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<>(); private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<>();
private final Channel channel; private final Channel channel;
private volatile ChannelHandler handler; private volatile ChannelHandler handler;
@ -61,13 +65,9 @@ public final class Http2StreamChannelBootstrap {
public <T> Http2StreamChannelBootstrap option(ChannelOption<T> option, T value) { public <T> Http2StreamChannelBootstrap option(ChannelOption<T> option, T value) {
requireNonNull(option, "option"); requireNonNull(option, "option");
if (value == null) { if (value == null) {
synchronized (options) { options.remove(option);
options.remove(option);
}
} else { } else {
synchronized (options) { options.put(option, value);
options.put(option, value);
}
} }
return this; return this;
} }
@ -80,13 +80,9 @@ public final class Http2StreamChannelBootstrap {
public <T> Http2StreamChannelBootstrap attr(AttributeKey<T> key, T value) { public <T> Http2StreamChannelBootstrap attr(AttributeKey<T> key, T value) {
requireNonNull(key, "key"); requireNonNull(key, "key");
if (value == null) { if (value == null) {
synchronized (attrs) { attrs.remove(key);
attrs.remove(key);
}
} else { } else {
synchronized (attrs) { attrs.put(key, value);
attrs.put(key, value);
}
} }
return this; return this;
} }
@ -193,36 +189,29 @@ public final class Http2StreamChannelBootstrap {
}); });
} }
@SuppressWarnings("unchecked")
private void init(Channel channel) { private void init(Channel channel) {
ChannelPipeline p = channel.pipeline(); ChannelPipeline p = channel.pipeline();
ChannelHandler handler = this.handler; ChannelHandler handler = this.handler;
if (handler != null) { if (handler != null) {
p.addLast(handler); p.addLast(handler);
} }
synchronized (options) { setChannelOptions(channel, options.entrySet().toArray(EMPTY_OPTION_ARRAY));
setChannelOptions(channel, options); setAttributes(channel, attrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
}
synchronized (attrs) {
for (Map.Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
} }
private static void setChannelOptions( private static void setChannelOptions(
Channel channel, Map<ChannelOption<?>, Object> options) { Channel channel, Map.Entry<ChannelOption<?>, Object>[] options) {
for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) { for (Map.Entry<ChannelOption<?>, Object> e: options) {
setChannelOption(channel, e.getKey(), e.getValue()); setChannelOption(channel, e.getKey(), e.getValue());
} }
} }
@SuppressWarnings("unchecked")
private static void setChannelOption( private static void setChannelOption(
Channel channel, ChannelOption<?> option, Object value) { Channel channel, ChannelOption<?> option, Object value) {
try { try {
if (!channel.config().setOption((ChannelOption<Object>) option, value)) { @SuppressWarnings("unchecked")
ChannelOption<Object> opt = (ChannelOption<Object>) option;
if (!channel.config().setOption(opt, value)) {
logger.warn("Unknown channel option '{}' for channel '{}'", option, channel); logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
} }
} catch (Throwable t) { } catch (Throwable t) {
@ -230,4 +219,13 @@ public final class Http2StreamChannelBootstrap {
"Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t); "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
} }
} }
private static void setAttributes(
Channel channel, Map.Entry<AttributeKey<?>, Object>[] options) {
for (Map.Entry<AttributeKey<?>, Object> e: options) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
} }

View File

@ -48,6 +48,10 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C, F>, C extends Channel, F> public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C, F>, C extends Channel, F>
implements Cloneable { implements Cloneable {
@SuppressWarnings("unchecked")
static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
@SuppressWarnings("unchecked")
static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
volatile EventLoopGroup group; volatile EventLoopGroup group;
private volatile SocketAddress localAddress; private volatile SocketAddress localAddress;
@ -353,16 +357,6 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C, F>, C
} }
} }
@SuppressWarnings("unchecked")
static Map.Entry<AttributeKey<?>, Object>[] newAttrArray(int size) {
return new Map.Entry[size];
}
@SuppressWarnings("unchecked")
static Map.Entry<ChannelOption<?>, Object>[] newOptionArray(int size) {
return new Map.Entry[size];
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static void setChannelOption( private static void setChannelOption(
Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) { Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {

View File

@ -277,8 +277,8 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel, ChannelFact
ChannelPipeline p = channel.pipeline(); ChannelPipeline p = channel.pipeline();
p.addLast(config.handler()); p.addLast(config.handler());
setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger); setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0))); setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
return promise.setSuccess(); return promise.setSuccess();
} }

View File

@ -160,15 +160,15 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
@Override @Override
ChannelFuture init(Channel channel) { ChannelFuture init(Channel channel) {
final ChannelPromise promise = channel.newPromise(); final ChannelPromise promise = channel.newPromise();
setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger); setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0))); setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
ChannelPipeline p = channel.pipeline(); ChannelPipeline p = channel.pipeline();
final ChannelHandler currentChildHandler = childHandler; final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions = final Entry<ChannelOption<?>, Object>[] currentChildOptions =
childOptions.entrySet().toArray(newOptionArray(0)); childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0)); final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
p.addLast(new ChannelInitializer<Channel>() { p.addLast(new ChannelInitializer<Channel>() {
@Override @Override