Skip to content

runtime: concurrent map read and map write with sync.RWMutex on ppc64le #79186

@jkrishmys

Description

@jkrishmys

Go version

go version go1.27-devel_3b5954c634 linux/ppc64le

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='ppc64le'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/user/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/user/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build438762286=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='ppc64le'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/user/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/user/go'
GOPPC64='power8'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/user/golang/godevel'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/user/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/user/golang/godevel/pkg/tool/linux_ppc64le'
GOVCS=''
GOVERSION='go1.27-devel_5bb6d165f0 Tue Apr 28 18:11:27 2026 -0700'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

I ran the following commands to run the test and reproduce the issue across multiple runs:

export GOMAXPROCS=2
go test -v -count=50 -run TestRWMutexMap

Here is the standalone reproducer used in rwmutex_map_test.go:

package repro

import (
	"sync"
	"testing"
)

type M struct {
	mu sync.RWMutex
	m  map[int]int
}

func NewM() *M {
	return &M{m: make(map[int]int)}
}

func (x *M) Get(k int) (int, bool) {
	x.mu.RLock()
	v, ok := x.m[k]
	x.mu.RUnlock()
	return v, ok
}

func (x *M) Set(k, v int) {
	x.mu.Lock()
	x.m[k] = v
	x.mu.Unlock()
}

func TestRWMutexMap(t *testing.T) {
	x := NewM()

	const goroutines = 256
	const iters = 200000

	var wg sync.WaitGroup
	wg.Add(goroutines)

	for g := 0; g < goroutines; g++ {
		go func(id int) {
			defer wg.Done()
			for i := 0; i < iters; i++ {
				k := (id + i) & 15
				if _, ok := x.Get(k); !ok {
					x.Set(k, i)
				} else if i&7 == 0 {
					x.Set(k, i)
				}
			}
		}(g)
	}

	wg.Wait()
}

What did you see happen?

Intermittent failures trigger the following fatal error:

fatal error: concurrent map read and map write

internal/runtime/maps.fatal
repro.(*M).Get
rwmutex_map_test.go:19

What did you expect to see?

The test should pass. sync.RWMutex should properly prevent concurrent map read/write conditions.

Additional context
Replacing sync.RWMutex with sync.Mutex eliminates the failure in my runs under the same workload.

// ...
type M struct {
	mu sync.Mutex
	m  map[int]int
}

func (x *M) Get(k int) (int, bool) {
	x.mu.Lock()
	v, ok := x.m[k]
	x.mu.Unlock()
	return v, ok
}
// ...

Results: The test passes consistently. No failures were observed under the same workload.

Environment Observations:

Reproduced on POWER8 and POWER9 (ppc64le).

Not reproduced on POWER10 (ppc64le).

Additional Notes:

The issue only reproduces when GOMAXPROCS > 1.

It does not reproduce with GOMAXPROCS = 1.

It does not reproduce under -race (likely due to additional synchronization and timing changes).

This issue was initially observed in real workloads (Kubernetes expiring cache tests) where replacing RWMutex with Mutex eliminated test flakes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.FixPendingIssues that have a fix which has not yet been reviewed or submitted.NeedsFixThe path to resolution is known, but the work has not been done.arch-ppc64xcompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions