gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
77.54k stars 7.95k forks source link

"concurrent map read and map write" in getting context #700

Open ntquyen opened 7 years ago

ntquyen commented 7 years ago

I got a race condition when getting value from Context: concurrent map read and map write

The potential error code is in /gin-gonic/gin/context.go

// Set is used to store a new key/value pair exclusivelly for this context.
// It also lazy initializes  c.Keys if it was not used previously.
func (c *Context) Set(key string, value interface{}) {
    if c.Keys == nil {
        c.Keys = make(map[string]interface{})
    }
    c.Keys[key] = value
}

// Get returns the value for the given key, ie: (value, true).
// If the value does not exists it returns (nil, false)
func (c *Context) Get(key string) (value interface{}, exists bool) {
    if c.Keys != nil {
        value, exists = c.Keys[key]
    }
    return
}

I thought here we must use Mutex around c.Keys[key] to avoid the race condition.

crazbot commented 7 years ago

is the Context often used as a container or something else?

ntquyen commented 7 years ago

Yes, it is used as a map to store header values and some request metrics

crazbot commented 7 years ago

okay, uh, so the Context is commonly used as a single entity. well, we should use Mutex in our logic instead of directly bind a Mutex to the Contexts.

stxml commented 7 years ago

Should we orient ourselves toward context.Context? It is safe to use concurrently and gin.Context implements the interface.

NeoCN commented 7 years ago

any update on this issue?

maxatome commented 7 years ago

As I just commented in pull request #702, putting a Mutex around c.Keys is a bad idea due to the recycling logic gin gonic applies to Context instances. You have to do a copy of the context (or of the Keys map) before sharing it, then handle the locking mechanism at your level.

NeoCN commented 7 years ago

Because of the recycling logic gin applies to Context, we can not simply use gin.Context and must do c.Copy for continue use(may be used in a goroutine), and this result that we do c.Copy nearly almost every method. Just thinking about we need an option to disable recycling logic for gin.Context.

themartorana commented 7 years ago

Not sure if anyone started getting this issue with Go 1.8, but we did - a lot. (We just deployed to production with 1.8 for the first time today.)

Looks like 1.8 added a new check. From https://stackoverflow.com/questions/42453442/concurrent-map-iteration-and-map-write-error-in-golang-1-8 :

There is a enhanced concurrent access check added in the Golang 1.8, and this is the source code in runtime/hashmap.go:736,

if h.flags&hashWriting != 0 {
    throw("concurrent map iteration and map write")
}

I'm trying to work around it right now, but I may be forced to toss a mutex lock into our vendor copy of Gin in the meantime (or roll back to 1.7)...

rkuska commented 4 years ago

FYI This issue should be closed gin.Context has a RWMutex for safeguarding since v1.6.0.

niboge commented 3 years ago

FYI This issue should be closed gin.Context has a RWMutex for safeguarding since v1.6.0.

set header 、 set response , is it safe too?

guoruibiao commented 3 years ago

I find this question @v1.4.0, question could go to sleep finally. Thx this issue.