Skip to content

runtime: maps behave inconsistently on arm64 and amd64 architectures when the -race detection is not enabled #69652

@Anthony-Dong

Description

@Anthony-Dong

Go version

go version go1.20 linux/amd64
go version go1.20 darwin/arm64

Output of go env in your module/workspace:

1. Mac-arm64(Apple M3 Pro)

GO111MODULE="on"
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/bytedance/Library/Caches/go-build"
GOENV="/Users/bytedance/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/bytedance/go/pkg/mod"
GOOS="darwin"
GOPATH="/Users/bytedance/go"
GOROOT="/Users/bytedance/software/go1.20"
GOSUMDB="sum.golang.google.cn"
GOTMPDIR=""
GOTOOLDIR="/Users/bytedance/software/go1.20/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="0"
GOMOD="/Users/bytedance/go/src/github.com/anthony-dong/golang/example/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/13/yc48l58j4j9_rrnff17jqdk00000gn/T/go-build1128814608=/tmp/go-build -gno-record-gcc-switches -fno-common"
  1. linux-amd64(Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz)
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/xxx/.cache/go-build"
GOENV="/home/xxx/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/xxx/go/pkg/mod"
GOOS="linux"
GOPATH="/home/xxx/go"
GOROOT="/home/xxx/software/go/go1.20"
GOTMPDIR=""
GOTOOLDIR="/home/xxx/software/go/go1.20/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/xxx/go/src/github.com/anthony-dong/golang/example/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build461736185=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I have a piece of code that behaves inconsistently on Mac-Apple M3 Pro and Linux amd64 when the -race detection is not enabled. On Mac-Apple M3 Pro, it directly reports a map rw error, but on Linux amd64, it doesn't.

package test_map

import (
	"sync"
	"testing"
)

type Map struct {
	kv map[string]interface{} // 8byte
}

func (m *Map) Get(key string) interface{} {
	return m.kv[key]
}

func (m *Map) clone() map[string]interface{} {
	res := make(map[string]interface{}, len(m.kv))
	for k, v := range m.kv {
		res[k] = v
	}
	return res
}

func (m *Map) Set(mm map[string]interface{}) {
	clone := m.clone()
	for k, v := range mm {
		clone[k] = v
	}
	m.kv = clone // replace
}

func TestMapRW(t *testing.T) {
	wg := sync.WaitGroup{}
	wg.Add(11)
	kv := Map{}
	kv.Set(map[string]interface{}{"1": 1})
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			for j := 0; j < 100000; j++ {
				kv.Get("1")
			}
		}()
	}

	for i := 0; i < 1; i++ {
		go func() {
			defer wg.Done()
			for j := 0; j < 100000; j++ {
				kv.Set(map[string]interface{}{"2": 2})
			}
		}()
	}

	wg.Wait()
}

What did you see happen?

  1. Linux
~ go test -run=TestMapRW -count=20 ./...
ok  	github.com/anthony-dong/golang/example/benchmark/test_map	0.802s
  1. Mac
~ go test -run=TestMapRW -count=20 ./...
fatal error: concurrent map read and map write

goroutine 30 [running]:
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).Get(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:13
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:41 +0x7c
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 1 [chan receive]:
testing.(*T).Run(0x140000829c0, {0x104994913?, 0x2b9544170e8d8?}, 0x1049f4060)
        /Users/bytedance/software/go1.20/src/testing/testing.go:1630 +0x384
testing.runTests.func1(0x140000829c0?)
        /Users/bytedance/software/go1.20/src/testing/testing.go:2036 +0x48
testing.tRunner(0x140000829c0, 0x1400014dc68)
        /Users/bytedance/software/go1.20/src/testing/testing.go:1576 +0x104
testing.runTests(0x1400012e0a0?, {0x104aa61c0, 0x1, 0x1}, {0x14000118800?, 0x40?, 0x104aae660?})
        /Users/bytedance/software/go1.20/src/testing/testing.go:2034 +0x3c4
testing.(*M).Run(0x1400012e0a0)
        /Users/bytedance/software/go1.20/src/testing/testing.go:1906 +0x530
main.main()
        _testmain.go:47 +0x1a8

goroutine 21 [semacquire]:
sync.runtime_Semacquire(0x140001169c0?)
        /Users/bytedance/software/go1.20/src/runtime/sema.go:62 +0x2c
sync.(*WaitGroup).Wait(0x1400011a120)
        /Users/bytedance/software/go1.20/src/sync/waitgroup.go:116 +0x74
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW(0x0?)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:55 +0x358
testing.tRunner(0x14000082b60, 0x1049f4060)
        /Users/bytedance/software/go1.20/src/testing/testing.go:1576 +0x104
created by testing.(*T).Run
        /Users/bytedance/software/go1.20/src/testing/testing.go:1629 +0x370

goroutine 22 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 23 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 24 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).Get(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:13
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:41 +0x7c
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 25 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 26 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 27 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 28 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).Get(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:13
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:41 +0x7c
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 29 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 31 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).Get(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:13
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func1()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:41 +0x7c
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:38 +0x274

goroutine 32 [runnable]:
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).clone(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:18
github.com/anthony-dong/golang/example/benchmark/test_map.(*Map).Set(...)
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:25
github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW.func2()
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:50 +0x170
created by github.com/anthony-dong/golang/example/benchmark/test_map.TestMapRW
        /Users/bytedance/go/src/github.com/anthony-dong/golang/example/benchmark/test_map/map_test.go:47 +0x2ec
FAIL    github.com/anthony-dong/golang/example/benchmark/test_map       0.723s
FAIL

What did you expect to see?

  1. Why do different chips behave inconsistently?
  2. If it is a memory race condition, it should report a memory access violation rather than a map rw error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions