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.

EXAMPLE · FRAGMENT UNLESS A COMPLETE PROGRAM IS SHOWN

go
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
  1. 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.

  2. 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.

  3. 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.