Skip to content

Data race: App.WebSocket writes ctx.Context while serveWithGoroutine reads it (Test_WebSocket_Success fails under -race) #3930

Description

@akshat-kumar-singhal

Describe the bug

App.WebSocket's handler writes ctx.Context while serveWithGoroutine concurrently reads
c.Context in its select, so every WebSocket request races the framework's own handler wrapper.

This is the websocket race deferred in 050596e ("The gRPC server ... and a pre-existing websocket
race are out of scope ... tracked separately"), which never got an issue. The gRPC half is #3929.

The race

Write, on the goroutine serveWithGoroutine spawned:

// pkg/gofr/websocket.go:41
ctx.Context = context.WithValue(ctx, websocket.WSConnectionKey, conn)

Read, on the goroutine that spawned it:

// pkg/gofr/handler.go:180
select {
case <-c.Context.Done():

serveWithGoroutine was written to avoid exactly this — its own comment says "shared variables, so
the goroutine never writes a memory location the main goroutine reads — go test -race stays
clean". That holds for the outcome value, which is passed over a channel, but not for c.Context:
the handler is handed the same *gofr.Context and is free to reassign the field, which
App.WebSocket does on every connection.

Relationship to #3823

Same class — an unsynchronised write to c.Context that another goroutine reads — but a different
write site and a different reader. #3823 is Context.Trace() racing user code in gRPC streaming
handlers; this one is framework code (App.WebSocket) racing framework code
(serveWithGoroutine), and it reproduces in this repo's own test suite with no user code involved.
Worth deciding together: a fix that makes c.Context safe to reassign (or removes the need to
reassign it) would close both. Filing separately rather than burying it in #3823's thread.

To Reproduce

go test ./pkg/gofr/ -race -count=1 -short -run 'Test_WebSocket_Success'

Reproduces on clean development (verified at ceb50390); --- FAIL: Test_WebSocket_Success ... race detected during execution of test.

Note go test ./pkg/gofr/websocket/... -race is green — the race is in the handler wiring in
pkg/gofr, not in the websocket package itself.

Expected behaviour

A WebSocket handler storing the connection on its context does not race the request wrapper, and
go test -race ./pkg/gofr/ is clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions