Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure ChannelHandlerContext.isRemoved is called only when in event loop #3031

Merged
merged 1 commit into from
Jan 18, 2024

Conversation

violetagg
Copy link
Member

Related to #2981

@violetagg violetagg added the type/bug A general bug label Jan 17, 2024
@violetagg violetagg added this to the 1.0.42 milestone Jan 17, 2024
@violetagg violetagg requested a review from a team January 17, 2024 14:37
@pderop
Copy link
Contributor

pderop commented Jan 17, 2024

How do you feel about reducing the code duplications between the http2FrameCodecCtx()/http2MultiplexHandlerCtx()/h2cUpgradeHandlerCtx() method:
instead of the current:

		@Nullable
		ChannelHandlerContext http2FrameCodecCtx() {
			ChannelHandlerContext ctx = http2FrameCodecCtx;
			// ChannelHandlerContext.isRemoved is only meant to be called from within the EventLoop
			if (ctx != null && connection.channel().eventLoop().inEventLoop() && !ctx.isRemoved()) {
				return ctx;
			}
			ctx = connection.channel().pipeline().context(Http2FrameCodec.class);
			http2FrameCodecCtx = ctx;
			return ctx;
		}

		@Nullable
		ChannelHandlerContext http2MultiplexHandlerCtx() {
			ChannelHandlerContext ctx = http2MultiplexHandlerCtx;
			// ChannelHandlerContext.isRemoved is only meant to be called from within the EventLoop
			if (ctx != null && connection.channel().eventLoop().inEventLoop() && !ctx.isRemoved()) {
				return ctx;
			}
			ctx = connection.channel().pipeline().context(Http2MultiplexHandler.class);
			http2MultiplexHandlerCtx = ctx;
			return ctx;
		}

		@Nullable
		ChannelHandlerContext h2cUpgradeHandlerCtx() {
			ChannelHandlerContext ctx = h2cUpgradeHandlerCtx;
			// ChannelHandlerContext.isRemoved is only meant to be called from within the EventLoop
			if (ctx != null && connection.channel().eventLoop().inEventLoop() && !ctx.isRemoved()) {
				return ctx;
			}
			ctx = connection.channel().pipeline().context(NettyPipeline.H2CUpgradeHandler);
			h2cUpgradeHandlerCtx = ctx;
			return ctx;
		}

then do a variant like this (I have verified by doing a benchmark and the performance is similar):

		@Nullable
		ChannelHandlerContext http2FrameCodecCtx() {
			return getCachedHandlerContext(http2FrameCodecCtx, () ->
					http2FrameCodecCtx = connection.channel().pipeline().context(Http2FrameCodec.class));
		}

		@Nullable
		ChannelHandlerContext http2MultiplexHandlerCtx() {
			return getCachedHandlerContext(http2MultiplexHandlerCtx, () ->
					http2MultiplexHandlerCtx = connection.channel().pipeline().context(Http2MultiplexHandler.class));
		}

		@Nullable
		ChannelHandlerContext h2cUpgradeHandlerCtx() {
			return getCachedHandlerContext(h2cUpgradeHandlerCtx, () ->
					h2cUpgradeHandlerCtx = connection.channel().pipeline().context(NettyPipeline.H2CUpgradeHandler));
		}

		@Nullable
		ChannelHandlerContext getCachedHandlerContext(ChannelHandlerContext ctx, Supplier<ChannelHandlerContext> getCtx) {
			// ChannelHandlerContext.isRemoved is only meant to be called from within the EventLoop
			if (ctx != null && connection.channel().eventLoop().inEventLoop() && !ctx.isRemoved()) {
				return ctx;
			}
			return getCtx.get();
		}

or the above could be considered in another enhancement PR ?
in any case, +1 for the patch.

@violetagg
Copy link
Member Author

violetagg commented Jan 17, 2024

I was thinking about this, but isn't this generating too many unnecessary objects in a hot path?

@pderop
Copy link
Contributor

pderop commented Jan 17, 2024

I just verified with a comparison benchmark, it's really similar, it even goes a bit faster, and lambda/invokedynamic do not allocate objects if I'm correct.
well, we can consider this in another PR on any doubts.

@violetagg
Copy link
Member Author

Interesting ... I might be missing something but you are using the fields of the enclosing class, aren't you?

invocation 1

Screenshot 2024-01-17 at 22 15 20

invocation 2

Screenshot 2024-01-17 at 22 15 35

@pderop
Copy link
Contributor

pderop commented Jan 17, 2024

Yes. to be clear, here is a similar thing, but with lambdas enclosed in braces:

		@Nullable
		ChannelHandlerContext http2FrameCodecCtx() {
			return getCachedHandlerContext(http2FrameCodecCtx, () -> {
				return http2FrameCodecCtx = connection.channel().pipeline().context(Http2FrameCodec.class);
			});
		}

		@Nullable
		ChannelHandlerContext http2MultiplexHandlerCtx() {
			return getCachedHandlerContext(http2MultiplexHandlerCtx, () -> {
				return http2MultiplexHandlerCtx = connection.channel().pipeline().context(Http2MultiplexHandler.class);
			});
		}

		@Nullable
		ChannelHandlerContext h2cUpgradeHandlerCtx() {
			return getCachedHandlerContext(h2cUpgradeHandlerCtx, () -> {
					return h2cUpgradeHandlerCtx = connection.channel().pipeline().context(NettyPipeline.H2CUpgradeHandler);
			});
		}

		@Nullable
		ChannelHandlerContext getCachedHandlerContext(ChannelHandlerContext ctx, Supplier<ChannelHandlerContext> getCtx) {
			// ChannelHandlerContext.isRemoved is only meant to be called from within the EventLoop
			if (ctx != null && connection.channel().eventLoop().inEventLoop() && !ctx.isRemoved()) {
				return ctx;
			}
			return getCtx.get();
		}

is there any concerns about the above or about the initial proposal ?
The intention was to avoid the code duplication, but maybe I unintentionally planted a coding potato 😂 ?
if this not clear, you can forget about it, and I can give another try in a future PR.

@pderop
Copy link
Contributor

pderop commented Jan 18, 2024

mmm, my bad, this morning, I was reviewing the previous comments, and the lambdas are, as you mentioned, accessing instance fields of the surrounding class, and if correct, this requires capturing the value of the this reference.
and we can see allocations are taking place from your screenshots.

Please forget about the proposed enhancement! 😊

@violetagg
Copy link
Member Author

@pderop Thanks for the review!

@violetagg violetagg merged commit 14095e7 into 1.0.x Jan 18, 2024
9 checks passed
@violetagg violetagg deleted the ctx-isremoved branch January 18, 2024 09:11
violetagg added a commit that referenced this pull request Jan 18, 2024
violetagg added a commit that referenced this pull request Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/bug A general bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants