add a thread safe context that can be used downstream and mutated #4172
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
It's currently unsafe to use
c.Request.Context
from the*gin.Context
to pass to downstream functions AND do any mutations to thec.Request.Context
.Even when you do
c.Request = c.Request.WithContext(...)
you'll end up with the go race detector flagging the unsafe reading of thec.Request
and writing toc.Request
.However it would be really nice to be able to put values into the
*gin.Context
withcontext.WithValue()
in a thread-safe way that allows downstream usage of*gin.Context
ascontext.Context
to access those values.For example, an auth middleware adding some debugging /loggin related values with
context.WithValue()
will not be lost along the way, or adding aotel.Tracer
to the context with additional options etc.Making all the places where gin touches
c.Request
thread-safe with a lock would be a significant undertaking and would have an impact on performance (probably talking about nano seconds, but still non zero impact).With the MR we're adding a thread-safe way to carry around and mutate a
context.Context
inside*gin.Context
without having to protectc.Request
.In practice the
c.Request.Context
isn't going to be very interesting to wrap anyway, so loosing that as the "root" context probably isn't a issue for anyone.It's trivial to make the root
InternalContext
thec.Request.Context
when we do.reset()
(ifContextWithFallback
is enabled), but I wanted to keep this MR as small as possible.And it would have one quirky downside that if someone uses
UseInternalContext
and does something likec.Request = c.Request.WithContext(...)
then it wouldn't have any effect.There's a couple of mentions of issues around this before:
DATA RACE
with Go1.21, Go1.22 #3931func (c *Context) Copy() *Context
#1118