Senior · Gin services
A goroutine captures the pooled Gin context
A handler launches go func(){ audit(c); c.JSON(...) }() and returns. Does c.Copy make this design safe?
Try explaining this aloud before opening the answer. State your assumptions and trace a boundary case.
package main
import (
"fmt"
"net/http/httptest"
"sync"
"github.com/gin-gonic/gin"
)
type Job struct{ Path string }
func main() {
gin.SetMode(gin.TestMode)
jobs := make(chan Job, 1)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
fmt.Println("work", job.Path)
}
}()
r := gin.New()
r.POST("/jobs", func(c *gin.Context) {
job := Job{Path: c.Request.URL.Path}
select {
case jobs <- job:
c.Status(202)
default:
c.Status(503)
}
})
w := httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest("POST", "/jobs", nil))
fmt.Println("accepted", w.Code)
close(jobs)
wg.Wait()
}Reveal a strong answer
Gin reuses contexts after a handler returns. Passing the original *gin.Context into a goroutine can observe another request’s mutable state. Copy is useful for limited read-only context access, but it does not extend the response writer’s lifetime, clone every referenced object or give detached work a durable owner. Prefer extracting immutable job input and handing it to a deliberately managed queue.
For best-effort work, state its lifetime and cancellation policy. For must-complete work, persist a job before acknowledging acceptance and use idempotent processing. Copy only immutable data required by the worker; slices and maps need an ownership decision. Request cancellation is appropriate for request work, while detached work needs an application-owned context with its own deadline and shutdown path. The handler sends a copied string in a small value and never lets the worker use c.Writer. The channel has a fixed capacity and the process joins its worker. Output order of accepted versus work is intentionally unspecified. This queue is in-memory: returning 202 here does not promise survival of a process crash.
A c.Copy call does not make asynchronous response writes safe or turn the request context into a process context. Unbounded goroutines are an unbounded queue. Sending to a channel while another owner closes it can panic. Test queue-full rejection, shutdown while a handler is enqueueing, duplicate jobs and process recovery separately from ordinary request success.
Common trap
Copy is not a response-lifetime, goroutine-ownership or durability primitive.
Try it
Replace it with extracted immutable job data, bounded admission and an application-owned worker; define what 202 means after a crash.
Worked solution
The original context is reused and the completed response writer must not be used asynchronously. Copy only helps limited read access and does not deep-copy every referenced value, extend the response or make a job durable. Extract the required input, choose request-bound or separately owned work and define queue capacity and shutdown. For must-complete acceptance, persist the job before responding and make its effect replay-safe.
Change the constraint
Who closes the queue while requests are still attempting to enqueue?
How did you do?
Self-assessment, not a compiler pass or interview score. Explain the reasoning unaided before marking understood.
Not marked. Progress stays on this browser, separate from course completion.
Primary references
Checked 2026-10-28. Use documentation for your pinned runtime, driver and tool version.