My code to invoke gin.Context

func HandlerResponse(ctx *gin.Context, response *model.APIResponse) {
    ctx.JSON(response.ErrorCode, response)
}

it randomly produce the following panic

fatal error: concurrent map iteration and map write
goroutine 31750576 [running]:
reflect.mapiternext(0x4a084f?)
        /usr/local/go/src/runtime/map.go:1380 +0x19
reflect.(*MapIter).Next(0xc1d3a210e0?)
        /usr/local/go/src/reflect/value.go:1846 +0x7e
encoding/json.mapEncoder.encode({0xc1d3a211a0?}, 0xc1768e4280, {0x1256000?, 0xc187a4bcc0?, 0x5?}, {0xe?, 0x0?})
        /usr/local/go/src/encoding/json/encode.go:798 +0x33e
encoding/json.structEncoder.encode({{{0xc035155300?, 0x404554?, 0xc000052500?}, 0xc1bfb0eb70?}}, 0xc1768e4280, {0x1396b40?, 0xc187a4bc20?, 0x4ed634?}, {0x0, 0x1})
        /usr/local/go/src/encoding/json/encode.go:760 +0x1f4
encoding/json.ptrEncoder.encode({0xc1d3a213d0?}, 0xc1768e4280, {0x11acda0?, 0xc187a4bc20?, 0x11acda0?}, {0xa8?, 0xa?})
        /usr/local/go/src/encoding/json/encode.go:944 +0x25e
encoding/json.(*encodeState).reflectValue(0x1233c40?, {0x11acda0?, 0xc187a4bc20?, 0xc19792b0a3?}, {0x40?, 0x5d?})
        /usr/local/go/src/encoding/json/encode.go:359 +0x78
encoding/json.interfaceEncoder(0xc1768e4280, {0x1233c40?, 0xc1fffc1790?, 0x7e?}, {0xd0?, 0x14?})
        /usr/local/go/src/encoding/json/encode.go:715 +0xc8
encoding/json.structEncoder.encode({{{0xc034f82d80?, 0x404554?, 0x668cf5?}, 0xc1bfb0e870?}}, 0xc1768e4280, {0x130d120?, 0xc1fffc1770?, 0x4d9460?}, {0x0, 0x1})
        /usr/local/go/src/encoding/json/encode.go:760 +0x1f4
encoding/json.ptrEncoder.encode({0x203025?}, 0xc1768e4280, {0x11acd60?, 0xc1fffc1770?, 0x11acd60?}, {0x0?, 0x0?})
        /usr/local/go/src/encoding/json/encode.go:944 +0x25e
encoding/json.(*encodeState).reflectValue(0xc1d3a21720?, {0x11acd60?, 0xc1fffc1770?, 0x14?}, {0xc0?, 0x15?})
        /usr/local/go/src/encoding/json/encode.go:359 +0x78
encoding/json.(*encodeState).marshal(0xc1d3a21778?, {0x11acd60?, 0xc1fffc1770?}, {0xb0?, 0x17?})
        /usr/local/go/src/encoding/json/encode.go:331 +0xfa
encoding/json.Marshal({0x11acd60, 0xc1fffc1770})
        /usr/local/go/src/encoding/json/encode.go:160 +0x45
github.com/gin-gonic/gin/render.WriteJSON({0x7f5672c044a8, 0xc03f571600}, {0x11acd60, 0xc1fffc1770})
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/render/json.go:68 +0x63
github.com/gin-gonic/gin/render.JSON.Render(...)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/render/json.go:57
github.com/gin-gonic/gin.(*Context).Render(0xc03f571600, 0xc8, {0x1625cc8, 0xc094a4aa40})
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/context.go:926 +0xf8
github.com/gin-gonic/gin.(*Context).JSON(...)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/context.go:971
gitlab.alipay-inc.com/huse/huse-apiserver/pkg/controller.HandlerResponse(...)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/pkg/controller/controller.go:19
gitlab.alipay-inc.com/huse/huse-apiserver/pkg/controller.(*HciBatchJobController).GetBatchJob(0xc0360e4de0, 0xc03f571600)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/pkg/controller/batchjob.go:110 +0xe9
github.com/gin-gonic/gin.(*Context).Next(...)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/context.go:174
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0360ce340, 0xc03f571600)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/gin.go:620 +0x671
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0360ce340, {0x1629830?, 0xc094a060e0}, 0xc091585000)
        /go/src/gitlab.alipay-inc.com/huse/huse-apiserver/vendor/github.com/gin-gonic/gin/gin.go:576 +0x1dd
net/http.serverHandler.ServeHTTP({0xc2e5517f20?}, {0x1629830, 0xc094a060e0}, 0xc091585000)
        /usr/local/go/src/net/http/server.go:2947 +0x30c
net/http.(*conn).serve(0xc17d164500, {0x162ab58, 0xc135437cb0})
        /usr/local/go/src/net/http/server.go:1991 +0x607
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3102 +0x4db

I am wondering how it happened.

Comment From: Folium1

@m1093782566 that is because you use multiple goroutines, and don't use mutex to prevent access to shared memory by multiple goroutines

Comment From: m1093782566

@Folium1 thanks for reply. My confuse is about the panic place, it's paniced in the json/encode from gin.(*Context).JSON(...). How should I prevent multiple goroutines accessing shared memory from calling

func HandlerResponse(ctx *gin.Context, response *model.APIResponse) {
    ctx.JSON(response.ErrorCode, response)
}

From my understanding, gin.Context is NOT shared by multiple goroutines.

Comment From: Folium1

@m1093782566 I guess that there is a map in model.APIResponse structure, that is a shared memory, i have tested that case, I got the same error. That is because when json tries to parse data, it needs to read it. That is why, we need to use sync primitives like mutex. Here is an example:

package main

import (
    "sync"
    "time"

    "github.com/gin-gonic/gin"
)

var mu sync.Mutex
var m = make(map[int]int)

func main() {
    r := gin.Default()
    go writeToMap()

    r.GET("/", t)
    r.Run(":8080")
}

func writeToMap() {
    i := 0
    for {
        mu.Lock()
        m[i] = i
        mu.Unlock()
        i++
        time.Sleep(10 * time.Millisecond)
    }
}

func t(c *gin.Context) {
    mu.Lock()
    defer mu.Unlock()
    c.JSON(200, m)
}

But if we try, to remove sync, we will get a panic.

Comment From: m1093782566

@Folium1 thank you for your example. There is a map in the model.APIResponse, that's true. I will try to deepcopy the model.APIResponse to prevent concurrent map read/write.