Initial commit: Go 1.23 release state
This commit is contained in:
17
src/runtime/race/README
Normal file
17
src/runtime/race/README
Normal file
@@ -0,0 +1,17 @@
|
||||
runtime/race package contains the data race detector runtime library.
|
||||
It is based on ThreadSanitizer race detector, that is currently a part of
|
||||
the LLVM project (https://github.com/llvm/llvm-project/tree/main/compiler-rt).
|
||||
|
||||
To update the .syso files use golang.org/x/build/cmd/racebuild.
|
||||
|
||||
internal/amd64v1/race_darwin.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
internal/amd64v1/race_freebsd.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
internal/amd64v1/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
internal/amd64v1/race_netbsd.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
internal/amd64v1/race_openbsd.syso built with LLVM fcf6ae2f070eba73074b6ec8d8281e54d29dbeeb and Go 8f2db14cd35bbd674cb2988a508306de6655e425.
|
||||
internal/amd64v1/race_windows.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
internal/amd64v3/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
race_darwin_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
race_linux_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
race_linux_ppc64le.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
race_linux_s390x.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5.
|
||||
11
src/runtime/race/doc.go
Normal file
11
src/runtime/race/doc.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package race implements data race detection logic.
|
||||
// No public interface is provided.
|
||||
// For details about the race detector see
|
||||
// https://golang.org/doc/articles/race_detector.html
|
||||
package race
|
||||
|
||||
//go:generate ./mkcgo.sh
|
||||
10
src/runtime/race/internal/amd64v1/doc.go
Normal file
10
src/runtime/race/internal/amd64v1/doc.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This package holds the race detector .syso for
|
||||
// amd64 architectures with GOAMD64<v3.
|
||||
|
||||
//go:build amd64 && ((linux && !amd64.v3) || darwin || freebsd || netbsd || openbsd || windows)
|
||||
|
||||
package amd64v1
|
||||
BIN
src/runtime/race/internal/amd64v1/race_darwin.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_darwin.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/internal/amd64v1/race_freebsd.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_freebsd.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/internal/amd64v1/race_linux.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_linux.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/internal/amd64v1/race_netbsd.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_netbsd.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/internal/amd64v1/race_openbsd.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_openbsd.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/internal/amd64v1/race_windows.syso
Normal file
BIN
src/runtime/race/internal/amd64v1/race_windows.syso
Normal file
Binary file not shown.
10
src/runtime/race/internal/amd64v3/doc.go
Normal file
10
src/runtime/race/internal/amd64v3/doc.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This package holds the race detector .syso for
|
||||
// amd64 architectures with GOAMD64>=v3.
|
||||
|
||||
//go:build amd64 && linux && amd64.v3
|
||||
|
||||
package amd64v3
|
||||
BIN
src/runtime/race/internal/amd64v3/race_linux.syso
Normal file
BIN
src/runtime/race/internal/amd64v3/race_linux.syso
Normal file
Binary file not shown.
20
src/runtime/race/mkcgo.sh
Executable file
20
src/runtime/race/mkcgo.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
hdr='
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by mkcgo.sh. DO NOT EDIT.
|
||||
|
||||
//go:build race
|
||||
|
||||
'
|
||||
|
||||
convert() {
|
||||
(echo "$hdr"; go tool cgo -dynpackage race -dynimport $1) | gofmt
|
||||
}
|
||||
|
||||
convert race_darwin_arm64.syso >race_darwin_arm64.go
|
||||
convert internal/amd64v1/race_darwin.syso >race_darwin_amd64.go
|
||||
|
||||
480
src/runtime/race/output_test.go
Normal file
480
src/runtime/race/output_test.go
Normal file
@@ -0,0 +1,480 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutput(t *testing.T) {
|
||||
pkgdir := t.TempDir()
|
||||
out, err := exec.Command(testenv.GoToolPath(t), "install", "-race", "-pkgdir="+pkgdir, "testing").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("go install -race: %v\n%s", err, out)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.goos != "" && test.goos != runtime.GOOS {
|
||||
t.Logf("test %v runs only on %v, skipping: ", test.name, test.goos)
|
||||
continue
|
||||
}
|
||||
dir := t.TempDir()
|
||||
source := "main.go"
|
||||
if test.run == "test" {
|
||||
source = "main_test.go"
|
||||
}
|
||||
src := filepath.Join(dir, source)
|
||||
f, err := os.Create(src)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create file: %v", err)
|
||||
}
|
||||
_, err = f.WriteString(test.source)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
t.Fatalf("failed to write: %v", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(testenv.GoToolPath(t), test.run, "-race", "-pkgdir="+pkgdir, src)
|
||||
// GODEBUG spoils program output, GOMAXPROCS makes it flaky.
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, "GODEBUG=") ||
|
||||
strings.HasPrefix(env, "GOMAXPROCS=") ||
|
||||
strings.HasPrefix(env, "GORACE=") {
|
||||
continue
|
||||
}
|
||||
cmd.Env = append(cmd.Env, env)
|
||||
}
|
||||
cmd.Env = append(cmd.Env,
|
||||
"GOMAXPROCS=1", // see comment in race_test.go
|
||||
"GORACE="+test.gorace,
|
||||
)
|
||||
got, _ := cmd.CombinedOutput()
|
||||
matched := false
|
||||
for _, re := range test.re {
|
||||
if regexp.MustCompile(re).MatchString(string(got)) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
exp := fmt.Sprintf("expect:\n%v\n", test.re[0])
|
||||
if len(test.re) > 1 {
|
||||
exp = fmt.Sprintf("expected one of %d patterns:\n",
|
||||
len(test.re))
|
||||
for k, re := range test.re {
|
||||
exp += fmt.Sprintf("pattern %d:\n%v\n", k, re)
|
||||
}
|
||||
}
|
||||
t.Fatalf("failed test case %v, %sgot:\n%s",
|
||||
test.name, exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
run string
|
||||
goos string
|
||||
gorace string
|
||||
source string
|
||||
re []string
|
||||
}{
|
||||
{"simple", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
import "time"
|
||||
var xptr *int
|
||||
var donechan chan bool
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
startRacer(&x, done)
|
||||
store(&x, 43)
|
||||
<-done
|
||||
}
|
||||
func store(x *int, v int) {
|
||||
*x = v
|
||||
}
|
||||
func startRacer(x *int, done chan bool) {
|
||||
xptr = x
|
||||
donechan = done
|
||||
go racer()
|
||||
}
|
||||
func racer() {
|
||||
time.Sleep(10*time.Millisecond)
|
||||
store(xptr, 42)
|
||||
donechan <- true
|
||||
}
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.store\(\)
|
||||
.+/main\.go:14 \+0x[0-9,a-f]+
|
||||
main\.racer\(\)
|
||||
.+/main\.go:23 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.store\(\)
|
||||
.+/main\.go:14 \+0x[0-9,a-f]+
|
||||
main\.main\(\)
|
||||
.+/main\.go:10 \+0x[0-9,a-f]+
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
main\.startRacer\(\)
|
||||
.+/main\.go:19 \+0x[0-9,a-f]+
|
||||
main\.main\(\)
|
||||
.+/main\.go:9 \+0x[0-9,a-f]+
|
||||
==================
|
||||
Found 1 data race\(s\)
|
||||
exit status 66
|
||||
`}},
|
||||
|
||||
{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
|
||||
package main
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
x := 0; _ = x
|
||||
go func() {
|
||||
x = 42
|
||||
done <- true
|
||||
}()
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, []string{`exit status 13`}},
|
||||
|
||||
{"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
|
||||
package main
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
x := 0; _ = x
|
||||
go func() {
|
||||
x = 42
|
||||
done <- true
|
||||
}()
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, []string{`
|
||||
go:7 \+0x[0-9,a-f]+
|
||||
`}},
|
||||
|
||||
{"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", `
|
||||
package main
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
x := 0; _ = x
|
||||
go func() {
|
||||
x = 42
|
||||
done <- true
|
||||
}()
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, []string{`
|
||||
==================
|
||||
exit status 66
|
||||
`}},
|
||||
|
||||
{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
|
||||
package main_test
|
||||
import "testing"
|
||||
func TestFail(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
_ = x
|
||||
go func() {
|
||||
x = 42
|
||||
done <- true
|
||||
}()
|
||||
x = 43
|
||||
<-done
|
||||
t.Log(t.Failed())
|
||||
}
|
||||
`, []string{`
|
||||
==================
|
||||
--- FAIL: TestFail \([0-9.]+s\)
|
||||
.*testing.go:.*: race detected during execution of test
|
||||
.*main_test.go:14: true
|
||||
FAIL`}},
|
||||
|
||||
{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
func main() {
|
||||
done := make(chan string)
|
||||
data := make([]byte, 10)
|
||||
go func() {
|
||||
done <- string(data)
|
||||
}()
|
||||
data[0] = 1
|
||||
<-done
|
||||
}
|
||||
`, []string{`
|
||||
runtime\.slicebytetostring\(\)
|
||||
.*/runtime/string\.go:.*
|
||||
main\.main\.func1\(\)
|
||||
.*/main.go:7`}},
|
||||
|
||||
// Test for https://golang.org/issue/33309
|
||||
{"midstack_inlining_traceback", "run", "linux", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
|
||||
var x int
|
||||
var c chan int
|
||||
func main() {
|
||||
c = make(chan int)
|
||||
go f()
|
||||
x = 1
|
||||
<-c
|
||||
}
|
||||
|
||||
func f() {
|
||||
g(c)
|
||||
}
|
||||
|
||||
func g(c chan int) {
|
||||
h(c)
|
||||
}
|
||||
|
||||
func h(c chan int) {
|
||||
c <- x
|
||||
}
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.h\(\)
|
||||
.+/main\.go:22 \+0x[0-9,a-f]+
|
||||
main\.g\(\)
|
||||
.+/main\.go:18 \+0x[0-9,a-f]+
|
||||
main\.f\(\)
|
||||
.+/main\.go:14 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.main\(\)
|
||||
.+/main\.go:9 \+0x[0-9,a-f]+
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
main\.main\(\)
|
||||
.+/main\.go:8 \+0x[0-9,a-f]+
|
||||
==================
|
||||
Found 1 data race\(s\)
|
||||
exit status 66
|
||||
`}},
|
||||
|
||||
// Test for https://golang.org/issue/17190
|
||||
{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <pthread.h>
|
||||
typedef struct cb {
|
||||
int foo;
|
||||
} cb;
|
||||
extern void goCallback();
|
||||
static inline void *threadFunc(void *p) {
|
||||
goCallback();
|
||||
return 0;
|
||||
}
|
||||
static inline void startThread(cb* c) {
|
||||
pthread_t th;
|
||||
pthread_create(&th, 0, threadFunc, 0);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
var done chan bool
|
||||
var racy int
|
||||
|
||||
//export goCallback
|
||||
func goCallback() {
|
||||
racy++
|
||||
done <- true
|
||||
}
|
||||
|
||||
func main() {
|
||||
done = make(chan bool)
|
||||
var c C.cb
|
||||
C.startThread(&c)
|
||||
racy++
|
||||
<- done
|
||||
}
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.main\(\)
|
||||
.*/main\.go:34 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.goCallback\(\)
|
||||
.*/main\.go:27 \+0x[0-9,a-f]+
|
||||
_cgoexp_[0-9a-z]+_goCallback\(\)
|
||||
.*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
_cgoexp_[0-9a-z]+_goCallback\(\)
|
||||
<autogenerated>:1 \+0x[0-9,a-f]+
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
runtime\.newextram\(\)
|
||||
.*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`,
|
||||
`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by .*:
|
||||
main\..*
|
||||
.*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by .*:
|
||||
main\..*
|
||||
.*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
runtime\.newextram\(\)
|
||||
.*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`}},
|
||||
{"second_test_passes", "test", "", "atexit_sleep_ms=0", `
|
||||
package main_test
|
||||
import "testing"
|
||||
func TestFail(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
_ = x
|
||||
go func() {
|
||||
x = 42
|
||||
done <- true
|
||||
}()
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestPass(t *testing.T) {
|
||||
}
|
||||
`, []string{`
|
||||
==================
|
||||
--- FAIL: TestFail \([0-9.]+s\)
|
||||
.*testing.go:.*: race detected during execution of test
|
||||
FAIL`}},
|
||||
{"mutex", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
import (
|
||||
"sync"
|
||||
"fmt"
|
||||
)
|
||||
func main() {
|
||||
c := make(chan bool, 1)
|
||||
threads := 1
|
||||
iterations := 20000
|
||||
data := 0
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < threads; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < iterations; i++ {
|
||||
c <- true
|
||||
data += 1
|
||||
<- c
|
||||
}
|
||||
}()
|
||||
}
|
||||
for i := 0; i < iterations; i++ {
|
||||
c <- true
|
||||
data += 1
|
||||
<- c
|
||||
}
|
||||
wg.Wait()
|
||||
if (data == iterations*(threads+1)) { fmt.Println("pass") }
|
||||
}`, []string{`pass`}},
|
||||
// Test for https://github.com/golang/go/issues/37355
|
||||
{"chanmm", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
func main() {
|
||||
c := make(chan bool, 1)
|
||||
var data uint64
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
c <- true
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(time.Second)
|
||||
<-c
|
||||
data = 2
|
||||
}()
|
||||
data = 1
|
||||
<-c
|
||||
wg.Wait()
|
||||
_ = data
|
||||
}
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.main\.func2\(\)
|
||||
.*/main\.go:21 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.main\(\)
|
||||
.*/main\.go:23 \+0x[0-9,a-f]+
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
main\.main\(\)
|
||||
.*/main.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`}},
|
||||
// Test symbolizing wrappers. Both (*T).f and main.gowrap1 are wrappers.
|
||||
// go.dev/issue/60245
|
||||
{"wrappersym", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
import "sync"
|
||||
var wg sync.WaitGroup
|
||||
var x int
|
||||
func main() {
|
||||
f := (*T).f
|
||||
wg.Add(2)
|
||||
go f(new(T))
|
||||
f(new(T))
|
||||
wg.Wait()
|
||||
}
|
||||
type T struct{}
|
||||
func (t T) f() {
|
||||
x = 42
|
||||
wg.Done()
|
||||
}
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.T\.f\(\)
|
||||
.*/main.go:15 \+0x[0-9,a-f]+
|
||||
main\.\(\*T\)\.f\(\)
|
||||
<autogenerated>:1 \+0x[0-9,a-f]+
|
||||
main\.main\.gowrap1\(\)
|
||||
.*/main.go:9 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.T\.f\(\)
|
||||
.*/main.go:15 \+0x[0-9,a-f]+
|
||||
main\.\(\*T\)\.f\(\)
|
||||
<autogenerated>:1 \+0x[0-9,a-f]+
|
||||
main\.main\(\)
|
||||
.*/main.go:10 \+0x[0-9,a-f]+
|
||||
|
||||
`}},
|
||||
}
|
||||
20
src/runtime/race/race.go
Normal file
20
src/runtime/race/race.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race && ((linux && (amd64 || arm64 || ppc64le || s390x)) || ((freebsd || netbsd || openbsd || windows) && amd64))
|
||||
|
||||
package race
|
||||
|
||||
// This file merely ensures that we link in runtime/cgo in race build,
|
||||
// this in turn ensures that runtime uses pthread_create to create threads.
|
||||
// The prebuilt race runtime lives in race_GOOS_GOARCH.syso.
|
||||
// Calls to the runtime are done directly from src/runtime/race.go.
|
||||
|
||||
// On darwin we always use system DLLs to create threads,
|
||||
// so we use race_darwin_$GOARCH.go to provide the syso-derived
|
||||
// symbol information without needing to invoke cgo.
|
||||
// This allows -race to be used on Mac systems without a C toolchain.
|
||||
|
||||
// void __race_unused_func(void);
|
||||
import "C"
|
||||
108
src/runtime/race/race_darwin_amd64.go
Normal file
108
src/runtime/race/race_darwin_amd64.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by mkcgo.sh. DO NOT EDIT.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race
|
||||
|
||||
//go:cgo_import_dynamic _Block_object_assign _Block_object_assign ""
|
||||
//go:cgo_import_dynamic _Block_object_dispose _Block_object_dispose ""
|
||||
//go:cgo_import_dynamic _NSConcreteStackBlock _NSConcreteStackBlock ""
|
||||
//go:cgo_import_dynamic _NSGetArgv _NSGetArgv ""
|
||||
//go:cgo_import_dynamic _NSGetEnviron _NSGetEnviron ""
|
||||
//go:cgo_import_dynamic _NSGetExecutablePath _NSGetExecutablePath ""
|
||||
//go:cgo_import_dynamic __bzero __bzero ""
|
||||
//go:cgo_import_dynamic __error __error ""
|
||||
//go:cgo_import_dynamic __fork __fork ""
|
||||
//go:cgo_import_dynamic __mmap __mmap ""
|
||||
//go:cgo_import_dynamic __munmap __munmap ""
|
||||
//go:cgo_import_dynamic __stack_chk_fail __stack_chk_fail ""
|
||||
//go:cgo_import_dynamic __stack_chk_guard __stack_chk_guard ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_header _dyld_get_image_header ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_name _dyld_get_image_name ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_vmaddr_slide _dyld_get_image_vmaddr_slide ""
|
||||
//go:cgo_import_dynamic _dyld_get_shared_cache_range _dyld_get_shared_cache_range ""
|
||||
//go:cgo_import_dynamic _dyld_get_shared_cache_uuid _dyld_get_shared_cache_uuid ""
|
||||
//go:cgo_import_dynamic _dyld_image_count _dyld_image_count ""
|
||||
//go:cgo_import_dynamic _exit _exit ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memcpy _sanitizer_internal_memcpy ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memmove _sanitizer_internal_memmove ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memset _sanitizer_internal_memset ""
|
||||
//go:cgo_import_dynamic abort abort ""
|
||||
//go:cgo_import_dynamic arc4random_buf arc4random_buf ""
|
||||
//go:cgo_import_dynamic close close ""
|
||||
//go:cgo_import_dynamic dlsym dlsym ""
|
||||
//go:cgo_import_dynamic dup dup ""
|
||||
//go:cgo_import_dynamic dup2 dup2 ""
|
||||
//go:cgo_import_dynamic dyld_shared_cache_iterate_text dyld_shared_cache_iterate_text ""
|
||||
//go:cgo_import_dynamic execve execve ""
|
||||
//go:cgo_import_dynamic exit exit ""
|
||||
//go:cgo_import_dynamic fstat$INODE64 fstat$INODE64 ""
|
||||
//go:cgo_import_dynamic ftruncate ftruncate ""
|
||||
//go:cgo_import_dynamic getpid getpid ""
|
||||
//go:cgo_import_dynamic getrlimit getrlimit ""
|
||||
//go:cgo_import_dynamic gettimeofday gettimeofday ""
|
||||
//go:cgo_import_dynamic getuid getuid ""
|
||||
//go:cgo_import_dynamic grantpt grantpt ""
|
||||
//go:cgo_import_dynamic ioctl ioctl ""
|
||||
//go:cgo_import_dynamic isatty isatty ""
|
||||
//go:cgo_import_dynamic lstat$INODE64 lstat$INODE64 ""
|
||||
//go:cgo_import_dynamic mach_absolute_time mach_absolute_time ""
|
||||
//go:cgo_import_dynamic mach_task_self_ mach_task_self_ ""
|
||||
//go:cgo_import_dynamic mach_timebase_info mach_timebase_info ""
|
||||
//go:cgo_import_dynamic mach_vm_region_recurse mach_vm_region_recurse ""
|
||||
//go:cgo_import_dynamic madvise madvise ""
|
||||
//go:cgo_import_dynamic malloc_num_zones malloc_num_zones ""
|
||||
//go:cgo_import_dynamic malloc_zones malloc_zones ""
|
||||
//go:cgo_import_dynamic memcpy memcpy ""
|
||||
//go:cgo_import_dynamic memset_pattern16 memset_pattern16 ""
|
||||
//go:cgo_import_dynamic mkdir mkdir ""
|
||||
//go:cgo_import_dynamic mprotect mprotect ""
|
||||
//go:cgo_import_dynamic open open ""
|
||||
//go:cgo_import_dynamic pipe pipe ""
|
||||
//go:cgo_import_dynamic posix_openpt posix_openpt ""
|
||||
//go:cgo_import_dynamic posix_spawn posix_spawn ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_addclose posix_spawn_file_actions_addclose ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_adddup2 posix_spawn_file_actions_adddup2 ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_destroy posix_spawn_file_actions_destroy ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_init posix_spawn_file_actions_init ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_destroy posix_spawnattr_destroy ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_init posix_spawnattr_init ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_setflags posix_spawnattr_setflags ""
|
||||
//go:cgo_import_dynamic pthread_attr_getstack pthread_attr_getstack ""
|
||||
//go:cgo_import_dynamic pthread_create pthread_create ""
|
||||
//go:cgo_import_dynamic pthread_get_stackaddr_np pthread_get_stackaddr_np ""
|
||||
//go:cgo_import_dynamic pthread_get_stacksize_np pthread_get_stacksize_np ""
|
||||
//go:cgo_import_dynamic pthread_getspecific pthread_getspecific ""
|
||||
//go:cgo_import_dynamic pthread_introspection_hook_install pthread_introspection_hook_install ""
|
||||
//go:cgo_import_dynamic pthread_join pthread_join ""
|
||||
//go:cgo_import_dynamic pthread_self pthread_self ""
|
||||
//go:cgo_import_dynamic pthread_sigmask pthread_sigmask ""
|
||||
//go:cgo_import_dynamic pthread_threadid_np pthread_threadid_np ""
|
||||
//go:cgo_import_dynamic read read ""
|
||||
//go:cgo_import_dynamic readlink readlink ""
|
||||
//go:cgo_import_dynamic realpath$DARWIN_EXTSN realpath$DARWIN_EXTSN ""
|
||||
//go:cgo_import_dynamic rename rename ""
|
||||
//go:cgo_import_dynamic sched_yield sched_yield ""
|
||||
//go:cgo_import_dynamic setrlimit setrlimit ""
|
||||
//go:cgo_import_dynamic sigaction sigaction ""
|
||||
//go:cgo_import_dynamic stat$INODE64 stat$INODE64 ""
|
||||
//go:cgo_import_dynamic sysconf sysconf ""
|
||||
//go:cgo_import_dynamic sysctl sysctl ""
|
||||
//go:cgo_import_dynamic sysctlbyname sysctlbyname ""
|
||||
//go:cgo_import_dynamic task_info task_info ""
|
||||
//go:cgo_import_dynamic tcgetattr tcgetattr ""
|
||||
//go:cgo_import_dynamic tcsetattr tcsetattr ""
|
||||
//go:cgo_import_dynamic unlink unlink ""
|
||||
//go:cgo_import_dynamic unlockpt unlockpt ""
|
||||
//go:cgo_import_dynamic usleep usleep ""
|
||||
//go:cgo_import_dynamic vm_region_64 vm_region_64 ""
|
||||
//go:cgo_import_dynamic vm_region_recurse_64 vm_region_recurse_64 ""
|
||||
//go:cgo_import_dynamic waitpid waitpid ""
|
||||
//go:cgo_import_dynamic write write ""
|
||||
//go:cgo_import_dynamic memcpy memcpy ""
|
||||
//go:cgo_import_dynamic memmove memmove ""
|
||||
//go:cgo_import_dynamic memset memset ""
|
||||
108
src/runtime/race/race_darwin_arm64.go
Normal file
108
src/runtime/race/race_darwin_arm64.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by mkcgo.sh. DO NOT EDIT.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race
|
||||
|
||||
//go:cgo_import_dynamic _Block_object_assign _Block_object_assign ""
|
||||
//go:cgo_import_dynamic _Block_object_dispose _Block_object_dispose ""
|
||||
//go:cgo_import_dynamic _NSConcreteStackBlock _NSConcreteStackBlock ""
|
||||
//go:cgo_import_dynamic _NSGetArgv _NSGetArgv ""
|
||||
//go:cgo_import_dynamic _NSGetEnviron _NSGetEnviron ""
|
||||
//go:cgo_import_dynamic _NSGetExecutablePath _NSGetExecutablePath ""
|
||||
//go:cgo_import_dynamic __error __error ""
|
||||
//go:cgo_import_dynamic __fork __fork ""
|
||||
//go:cgo_import_dynamic __mmap __mmap ""
|
||||
//go:cgo_import_dynamic __munmap __munmap ""
|
||||
//go:cgo_import_dynamic __stack_chk_fail __stack_chk_fail ""
|
||||
//go:cgo_import_dynamic __stack_chk_guard __stack_chk_guard ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_header _dyld_get_image_header ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_name _dyld_get_image_name ""
|
||||
//go:cgo_import_dynamic _dyld_get_image_vmaddr_slide _dyld_get_image_vmaddr_slide ""
|
||||
//go:cgo_import_dynamic _dyld_get_shared_cache_range _dyld_get_shared_cache_range ""
|
||||
//go:cgo_import_dynamic _dyld_get_shared_cache_uuid _dyld_get_shared_cache_uuid ""
|
||||
//go:cgo_import_dynamic _dyld_image_count _dyld_image_count ""
|
||||
//go:cgo_import_dynamic _exit _exit ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memcpy _sanitizer_internal_memcpy ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memmove _sanitizer_internal_memmove ""
|
||||
//go:cgo_import_dynamic _sanitizer_internal_memset _sanitizer_internal_memset ""
|
||||
//go:cgo_import_dynamic abort abort ""
|
||||
//go:cgo_import_dynamic arc4random_buf arc4random_buf ""
|
||||
//go:cgo_import_dynamic bzero bzero ""
|
||||
//go:cgo_import_dynamic close close ""
|
||||
//go:cgo_import_dynamic dlsym dlsym ""
|
||||
//go:cgo_import_dynamic dup dup ""
|
||||
//go:cgo_import_dynamic dup2 dup2 ""
|
||||
//go:cgo_import_dynamic dyld_shared_cache_iterate_text dyld_shared_cache_iterate_text ""
|
||||
//go:cgo_import_dynamic execve execve ""
|
||||
//go:cgo_import_dynamic exit exit ""
|
||||
//go:cgo_import_dynamic fstat fstat ""
|
||||
//go:cgo_import_dynamic ftruncate ftruncate ""
|
||||
//go:cgo_import_dynamic getpid getpid ""
|
||||
//go:cgo_import_dynamic getrlimit getrlimit ""
|
||||
//go:cgo_import_dynamic gettimeofday gettimeofday ""
|
||||
//go:cgo_import_dynamic getuid getuid ""
|
||||
//go:cgo_import_dynamic grantpt grantpt ""
|
||||
//go:cgo_import_dynamic ioctl ioctl ""
|
||||
//go:cgo_import_dynamic isatty isatty ""
|
||||
//go:cgo_import_dynamic lstat lstat ""
|
||||
//go:cgo_import_dynamic mach_absolute_time mach_absolute_time ""
|
||||
//go:cgo_import_dynamic mach_task_self_ mach_task_self_ ""
|
||||
//go:cgo_import_dynamic mach_timebase_info mach_timebase_info ""
|
||||
//go:cgo_import_dynamic mach_vm_region_recurse mach_vm_region_recurse ""
|
||||
//go:cgo_import_dynamic madvise madvise ""
|
||||
//go:cgo_import_dynamic malloc_num_zones malloc_num_zones ""
|
||||
//go:cgo_import_dynamic malloc_zones malloc_zones ""
|
||||
//go:cgo_import_dynamic memcpy memcpy ""
|
||||
//go:cgo_import_dynamic memset_pattern16 memset_pattern16 ""
|
||||
//go:cgo_import_dynamic mkdir mkdir ""
|
||||
//go:cgo_import_dynamic mprotect mprotect ""
|
||||
//go:cgo_import_dynamic open open ""
|
||||
//go:cgo_import_dynamic pipe pipe ""
|
||||
//go:cgo_import_dynamic posix_openpt posix_openpt ""
|
||||
//go:cgo_import_dynamic posix_spawn posix_spawn ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_addclose posix_spawn_file_actions_addclose ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_adddup2 posix_spawn_file_actions_adddup2 ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_destroy posix_spawn_file_actions_destroy ""
|
||||
//go:cgo_import_dynamic posix_spawn_file_actions_init posix_spawn_file_actions_init ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_destroy posix_spawnattr_destroy ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_init posix_spawnattr_init ""
|
||||
//go:cgo_import_dynamic posix_spawnattr_setflags posix_spawnattr_setflags ""
|
||||
//go:cgo_import_dynamic pthread_attr_getstack pthread_attr_getstack ""
|
||||
//go:cgo_import_dynamic pthread_create pthread_create ""
|
||||
//go:cgo_import_dynamic pthread_get_stackaddr_np pthread_get_stackaddr_np ""
|
||||
//go:cgo_import_dynamic pthread_get_stacksize_np pthread_get_stacksize_np ""
|
||||
//go:cgo_import_dynamic pthread_getspecific pthread_getspecific ""
|
||||
//go:cgo_import_dynamic pthread_introspection_hook_install pthread_introspection_hook_install ""
|
||||
//go:cgo_import_dynamic pthread_join pthread_join ""
|
||||
//go:cgo_import_dynamic pthread_self pthread_self ""
|
||||
//go:cgo_import_dynamic pthread_sigmask pthread_sigmask ""
|
||||
//go:cgo_import_dynamic pthread_threadid_np pthread_threadid_np ""
|
||||
//go:cgo_import_dynamic read read ""
|
||||
//go:cgo_import_dynamic readlink readlink ""
|
||||
//go:cgo_import_dynamic realpath$DARWIN_EXTSN realpath$DARWIN_EXTSN ""
|
||||
//go:cgo_import_dynamic rename rename ""
|
||||
//go:cgo_import_dynamic sched_yield sched_yield ""
|
||||
//go:cgo_import_dynamic setrlimit setrlimit ""
|
||||
//go:cgo_import_dynamic sigaction sigaction ""
|
||||
//go:cgo_import_dynamic stat stat ""
|
||||
//go:cgo_import_dynamic sysconf sysconf ""
|
||||
//go:cgo_import_dynamic sysctl sysctl ""
|
||||
//go:cgo_import_dynamic sysctlbyname sysctlbyname ""
|
||||
//go:cgo_import_dynamic task_info task_info ""
|
||||
//go:cgo_import_dynamic tcgetattr tcgetattr ""
|
||||
//go:cgo_import_dynamic tcsetattr tcsetattr ""
|
||||
//go:cgo_import_dynamic unlink unlink ""
|
||||
//go:cgo_import_dynamic unlockpt unlockpt ""
|
||||
//go:cgo_import_dynamic usleep usleep ""
|
||||
//go:cgo_import_dynamic vm_region_64 vm_region_64 ""
|
||||
//go:cgo_import_dynamic vm_region_recurse_64 vm_region_recurse_64 ""
|
||||
//go:cgo_import_dynamic waitpid waitpid ""
|
||||
//go:cgo_import_dynamic write write ""
|
||||
//go:cgo_import_dynamic memcpy memcpy ""
|
||||
//go:cgo_import_dynamic memmove memmove ""
|
||||
//go:cgo_import_dynamic memset memset ""
|
||||
BIN
src/runtime/race/race_darwin_arm64.syso
Normal file
BIN
src/runtime/race/race_darwin_arm64.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/race_linux_arm64.syso
Normal file
BIN
src/runtime/race/race_linux_arm64.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/race_linux_ppc64le.syso
Normal file
BIN
src/runtime/race/race_linux_ppc64le.syso
Normal file
Binary file not shown.
BIN
src/runtime/race/race_linux_s390x.syso
Normal file
BIN
src/runtime/race/race_linux_s390x.syso
Normal file
Binary file not shown.
65
src/runtime/race/race_linux_test.go
Normal file
65
src/runtime/race/race_linux_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && race
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestAtomicMmap(t *testing.T) {
|
||||
// Test that atomic operations work on "external" memory. Previously they crashed (#16206).
|
||||
// Also do a sanity correctness check: under race detector atomic operations
|
||||
// are implemented inside of race runtime.
|
||||
mem, err := syscall.Mmap(-1, 0, 1<<20, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("mmap failed: %v", err)
|
||||
}
|
||||
defer syscall.Munmap(mem)
|
||||
a := (*uint64)(unsafe.Pointer(&mem[0]))
|
||||
if *a != 0 {
|
||||
t.Fatalf("bad atomic value: %v, want 0", *a)
|
||||
}
|
||||
atomic.AddUint64(a, 1)
|
||||
if *a != 1 {
|
||||
t.Fatalf("bad atomic value: %v, want 1", *a)
|
||||
}
|
||||
atomic.AddUint64(a, 1)
|
||||
if *a != 2 {
|
||||
t.Fatalf("bad atomic value: %v, want 2", *a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtomicPageBoundary(t *testing.T) {
|
||||
// Test that atomic access near (but not cross) a page boundary
|
||||
// doesn't fault. See issue 60825.
|
||||
|
||||
// Mmap two pages of memory, and make the second page inaccessible,
|
||||
// so we have an address at the end of a page.
|
||||
pagesize := syscall.Getpagesize()
|
||||
b, err := syscall.Mmap(0, 0, 2*pagesize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("mmap failed %s", err)
|
||||
}
|
||||
defer syscall.Munmap(b)
|
||||
err = syscall.Mprotect(b[pagesize:], syscall.PROT_NONE)
|
||||
if err != nil {
|
||||
t.Fatalf("mprotect high failed %s\n", err)
|
||||
}
|
||||
|
||||
// This should not fault.
|
||||
a := (*uint32)(unsafe.Pointer(&b[pagesize-4]))
|
||||
atomic.StoreUint32(a, 1)
|
||||
if x := atomic.LoadUint32(a); x != 1 {
|
||||
t.Fatalf("bad atomic value: %v, want 1", x)
|
||||
}
|
||||
if x := atomic.AddUint32(a, 1); x != 2 {
|
||||
t.Fatalf("bad atomic value: %v, want 2", x)
|
||||
}
|
||||
}
|
||||
250
src/runtime/race/race_test.go
Normal file
250
src/runtime/race/race_test.go
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
|
||||
// This program is used to verify the race detector
|
||||
// by running the tests and parsing their output.
|
||||
// It does not check stack correctness, completeness or anything else:
|
||||
// it merely verifies that if a test is expected to be racy
|
||||
// then the race is detected.
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
passedTests = 0
|
||||
totalTests = 0
|
||||
falsePos = 0
|
||||
falseNeg = 0
|
||||
failingPos = 0
|
||||
failingNeg = 0
|
||||
failed = false
|
||||
)
|
||||
|
||||
const (
|
||||
visibleLen = 40
|
||||
testPrefix = "=== RUN Test"
|
||||
)
|
||||
|
||||
func TestRace(t *testing.T) {
|
||||
testOutput, err := runTests(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
|
||||
}
|
||||
reader := bufio.NewReader(bytes.NewReader(testOutput))
|
||||
|
||||
funcName := ""
|
||||
var tsanLog []string
|
||||
for {
|
||||
s, err := nextLine(reader)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", processLog(funcName, tsanLog))
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(s, testPrefix) {
|
||||
fmt.Printf("%s\n", processLog(funcName, tsanLog))
|
||||
tsanLog = make([]string, 0, 100)
|
||||
funcName = s[len(testPrefix):]
|
||||
} else {
|
||||
tsanLog = append(tsanLog, s)
|
||||
}
|
||||
}
|
||||
|
||||
if totalTests == 0 {
|
||||
t.Fatalf("failed to parse test output:\n%s", testOutput)
|
||||
}
|
||||
fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
|
||||
passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
|
||||
fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
|
||||
if failed {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// nextLine is a wrapper around bufio.Reader.ReadString.
|
||||
// It reads a line up to the next '\n' character. Error
|
||||
// is non-nil if there are no lines left, and nil
|
||||
// otherwise.
|
||||
func nextLine(r *bufio.Reader) (string, error) {
|
||||
s, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Fatalf("nextLine: expected EOF, received %v", err)
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
return s[:len(s)-1], nil
|
||||
}
|
||||
|
||||
// processLog verifies whether the given ThreadSanitizer's log
|
||||
// contains a race report, checks this information against
|
||||
// the name of the testcase and returns the result of this
|
||||
// comparison.
|
||||
func processLog(testName string, tsanLog []string) string {
|
||||
if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
|
||||
return ""
|
||||
}
|
||||
gotRace := false
|
||||
for _, s := range tsanLog {
|
||||
if strings.Contains(s, "DATA RACE") {
|
||||
gotRace = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
failing := strings.Contains(testName, "Failing")
|
||||
expRace := !strings.HasPrefix(testName, "No")
|
||||
for len(testName) < visibleLen {
|
||||
testName += " "
|
||||
}
|
||||
if expRace == gotRace {
|
||||
passedTests++
|
||||
totalTests++
|
||||
if failing {
|
||||
failed = true
|
||||
failingNeg++
|
||||
}
|
||||
return fmt.Sprintf("%s .", testName)
|
||||
}
|
||||
pos := ""
|
||||
if expRace {
|
||||
falseNeg++
|
||||
} else {
|
||||
falsePos++
|
||||
pos = "+"
|
||||
}
|
||||
if failing {
|
||||
failingPos++
|
||||
} else {
|
||||
failed = true
|
||||
}
|
||||
totalTests++
|
||||
return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
|
||||
}
|
||||
|
||||
// runTests assures that the package and its dependencies is
|
||||
// built with instrumentation enabled and returns the output of 'go test'
|
||||
// which includes possible data race reports from ThreadSanitizer.
|
||||
func runTests(t *testing.T) ([]byte, error) {
|
||||
tests, err := filepath.Glob("./testdata/*_test.go")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args := []string{"test", "-race", "-v"}
|
||||
args = append(args, tests...)
|
||||
cmd := exec.Command(testenv.GoToolPath(t), args...)
|
||||
// The following flags turn off heuristics that suppress seemingly identical reports.
|
||||
// It is required because the tests contain a lot of data races on the same addresses
|
||||
// (the tests are simple and the memory is constantly reused).
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, "GOMAXPROCS=") ||
|
||||
strings.HasPrefix(env, "GODEBUG=") ||
|
||||
strings.HasPrefix(env, "GORACE=") {
|
||||
continue
|
||||
}
|
||||
cmd.Env = append(cmd.Env, env)
|
||||
}
|
||||
// We set GOMAXPROCS=1 to prevent test flakiness.
|
||||
// There are two sources of flakiness:
|
||||
// 1. Some tests rely on particular execution order.
|
||||
// If the order is different, race does not happen at all.
|
||||
// 2. Ironically, ThreadSanitizer runtime contains a logical race condition
|
||||
// that can lead to false negatives if racy accesses happen literally at the same time.
|
||||
// Tests used to work reliably in the good old days of GOMAXPROCS=1.
|
||||
// So let's set it for now. A more reliable solution is to explicitly annotate tests
|
||||
// with required execution order by means of a special "invisible" synchronization primitive
|
||||
// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
|
||||
cmd.Env = append(cmd.Env,
|
||||
"GOMAXPROCS=1",
|
||||
"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
|
||||
)
|
||||
// There are races: we expect tests to fail and the exit code to be non-zero.
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if bytes.Contains(out, []byte("fatal error:")) {
|
||||
// But don't expect runtime to crash.
|
||||
return out, fmt.Errorf("runtime fatal error")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func TestIssue8102(t *testing.T) {
|
||||
// If this compiles with -race, the test passes.
|
||||
type S struct {
|
||||
x any
|
||||
i int
|
||||
}
|
||||
c := make(chan int)
|
||||
a := [2]*int{}
|
||||
for ; ; c <- *a[S{}.i] {
|
||||
if t != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue9137(t *testing.T) {
|
||||
a := []string{"a"}
|
||||
i := 0
|
||||
a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1]
|
||||
if len(a) != 0 || a[:1][0] != "" {
|
||||
t.Errorf("mangled a: %q %q", a, a[:1])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSyncLeak(b *testing.B) {
|
||||
const (
|
||||
G = 1000
|
||||
S = 1000
|
||||
H = 10
|
||||
)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(G)
|
||||
for g := 0; g < G; g++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
hold := make([][]uint32, H)
|
||||
for i := 0; i < b.N; i++ {
|
||||
a := make([]uint32, S)
|
||||
atomic.AddUint32(&a[rand.Intn(len(a))], 1)
|
||||
hold[rand.Intn(len(hold))] = a
|
||||
}
|
||||
_ = hold
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkStackLeak(b *testing.B) {
|
||||
done := make(chan bool, 1)
|
||||
for i := 0; i < b.N; i++ {
|
||||
go func() {
|
||||
growStack(rand.Intn(100))
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
func growStack(i int) {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
growStack(i - 1)
|
||||
}
|
||||
29
src/runtime/race/race_unix_test.go
Normal file
29
src/runtime/race/race_unix_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race && (darwin || freebsd || linux)
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Test that race detector does not crash when accessing non-Go allocated memory (issue 9136).
|
||||
func TestNonGoMemory(t *testing.T) {
|
||||
data, err := syscall.Mmap(-1, 0, 4096, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to mmap memory: %v", err)
|
||||
}
|
||||
defer syscall.Munmap(data)
|
||||
p := (*uint32)(unsafe.Pointer(&data[0]))
|
||||
atomic.AddUint32(p, 1)
|
||||
(*p)++
|
||||
if *p != 2 {
|
||||
t.Fatalf("data[0] = %v, expect 2", *p)
|
||||
}
|
||||
}
|
||||
9
src/runtime/race/race_v1_amd64.go
Normal file
9
src/runtime/race/race_v1_amd64.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (linux && !amd64.v3) || darwin || freebsd || netbsd || openbsd || windows
|
||||
|
||||
package race
|
||||
|
||||
import _ "runtime/race/internal/amd64v1"
|
||||
9
src/runtime/race/race_v3_amd64.go
Normal file
9
src/runtime/race/race_v3_amd64.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && amd64.v3
|
||||
|
||||
package race
|
||||
|
||||
import _ "runtime/race/internal/amd64v3"
|
||||
46
src/runtime/race/race_windows_test.go
Normal file
46
src/runtime/race/race_windows_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && race
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestAtomicMmap(t *testing.T) {
|
||||
// Test that atomic operations work on "external" memory. Previously they crashed (#16206).
|
||||
// Also do a sanity correctness check: under race detector atomic operations
|
||||
// are implemented inside of race runtime.
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
VirtualAlloc := kernel32.NewProc("VirtualAlloc")
|
||||
VirtualFree := kernel32.NewProc("VirtualFree")
|
||||
const (
|
||||
MEM_COMMIT = 0x00001000
|
||||
MEM_RESERVE = 0x00002000
|
||||
MEM_RELEASE = 0x8000
|
||||
PAGE_READWRITE = 0x04
|
||||
)
|
||||
mem, _, err := syscall.Syscall6(VirtualAlloc.Addr(), 4, 0, 1<<20, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE, 0, 0)
|
||||
if err != 0 {
|
||||
t.Fatalf("VirtualAlloc failed: %v", err)
|
||||
}
|
||||
defer syscall.Syscall(VirtualFree.Addr(), 3, mem, 1<<20, MEM_RELEASE)
|
||||
a := (*uint64)(unsafe.Pointer(mem))
|
||||
if *a != 0 {
|
||||
t.Fatalf("bad atomic value: %v, want 0", *a)
|
||||
}
|
||||
atomic.AddUint64(a, 1)
|
||||
if *a != 1 {
|
||||
t.Fatalf("bad atomic value: %v, want 1", *a)
|
||||
}
|
||||
atomic.AddUint64(a, 1)
|
||||
if *a != 2 {
|
||||
t.Fatalf("bad atomic value: %v, want 2", *a)
|
||||
}
|
||||
}
|
||||
48
src/runtime/race/sched_test.go
Normal file
48
src/runtime/race/sched_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandomScheduling(t *testing.T) {
|
||||
// Scheduler is most consistent with GOMAXPROCS=1.
|
||||
// Use that to make the test most likely to fail.
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
||||
const N = 10
|
||||
out := make([][]int, N)
|
||||
for i := 0; i < N; i++ {
|
||||
c := make(chan int, N)
|
||||
for j := 0; j < N; j++ {
|
||||
go func(j int) {
|
||||
c <- j
|
||||
}(j)
|
||||
}
|
||||
row := make([]int, N)
|
||||
for j := 0; j < N; j++ {
|
||||
row[j] = <-c
|
||||
}
|
||||
out[i] = row
|
||||
}
|
||||
|
||||
for i := 0; i < N; i++ {
|
||||
if !reflect.DeepEqual(out[0], out[i]) {
|
||||
return // found a different order
|
||||
}
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
for i := 0; i < N; i++ {
|
||||
fmt.Fprintf(&buf, "%v\n", out[i])
|
||||
}
|
||||
t.Fatalf("consistent goroutine execution order:\n%v", buf.String())
|
||||
}
|
||||
33
src/runtime/race/syso_test.go
Normal file
33
src/runtime/race/syso_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIssue37485(t *testing.T) {
|
||||
files, err := filepath.Glob("./*.syso")
|
||||
if err != nil {
|
||||
t.Fatalf("can't find syso files: %s", err)
|
||||
}
|
||||
for _, f := range files {
|
||||
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "nm", f)
|
||||
res, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("nm of %s failed: %s", f, err)
|
||||
continue
|
||||
}
|
||||
if bytes.Contains(res, []byte("getauxval")) {
|
||||
t.Errorf("%s contains getauxval", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
325
src/runtime/race/testdata/atomic_test.go
vendored
Normal file
325
src/runtime/race/testdata/atomic_test.go
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestNoRaceAtomicAddInt64(t *testing.T) {
|
||||
var x1, x2 int8
|
||||
_ = x1 + x2
|
||||
var s int64
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x1 = 1
|
||||
if atomic.AddInt64(&s, 1) == 2 {
|
||||
x2 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
x2 = 1
|
||||
if atomic.AddInt64(&s, 1) == 2 {
|
||||
x1 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceAtomicAddInt64(t *testing.T) {
|
||||
var x1, x2 int8
|
||||
_ = x1 + x2
|
||||
var s int64
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x1 = 1
|
||||
if atomic.AddInt64(&s, 1) == 1 {
|
||||
x2 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
x2 = 1
|
||||
if atomic.AddInt64(&s, 1) == 1 {
|
||||
x1 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicAddInt32(t *testing.T) {
|
||||
var x1, x2 int8
|
||||
_ = x1 + x2
|
||||
var s int32
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x1 = 1
|
||||
if atomic.AddInt32(&s, 1) == 2 {
|
||||
x2 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
x2 = 1
|
||||
if atomic.AddInt32(&s, 1) == 2 {
|
||||
x1 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicLoadAddInt32(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s int32
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.AddInt32(&s, 1)
|
||||
}()
|
||||
for atomic.LoadInt32(&s) != 1 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicLoadStoreInt32(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s int32
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.StoreInt32(&s, 1)
|
||||
}()
|
||||
for atomic.LoadInt32(&s) != 1 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicStoreCASInt32(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s int32
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.StoreInt32(&s, 1)
|
||||
}()
|
||||
for !atomic.CompareAndSwapInt32(&s, 1, 0) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicCASLoadInt32(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s int32
|
||||
go func() {
|
||||
x = 2
|
||||
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
|
||||
panic("")
|
||||
}
|
||||
}()
|
||||
for atomic.LoadInt32(&s) != 1 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicCASCASInt32(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s int32
|
||||
go func() {
|
||||
x = 2
|
||||
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
|
||||
panic("")
|
||||
}
|
||||
}()
|
||||
for !atomic.CompareAndSwapInt32(&s, 1, 0) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicCASCASInt32_2(t *testing.T) {
|
||||
var x1, x2 int8
|
||||
_ = x1 + x2
|
||||
var s int32
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x1 = 1
|
||||
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
|
||||
x2 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
x2 = 1
|
||||
if !atomic.CompareAndSwapInt32(&s, 0, 1) {
|
||||
x1 = 1
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicLoadInt64(t *testing.T) {
|
||||
var x int32
|
||||
_ = x
|
||||
var s int64
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.AddInt64(&s, 1)
|
||||
}()
|
||||
for atomic.LoadInt64(&s) != 1 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicCASCASUInt64(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s uint64
|
||||
go func() {
|
||||
x = 2
|
||||
if !atomic.CompareAndSwapUint64(&s, 0, 1) {
|
||||
panic("")
|
||||
}
|
||||
}()
|
||||
for !atomic.CompareAndSwapUint64(&s, 1, 0) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicLoadStorePointer(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s unsafe.Pointer
|
||||
var y int = 2
|
||||
var p unsafe.Pointer = unsafe.Pointer(&y)
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.StorePointer(&s, p)
|
||||
}()
|
||||
for atomic.LoadPointer(&s) != p {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestNoRaceAtomicStoreCASUint64(t *testing.T) {
|
||||
var x int64
|
||||
_ = x
|
||||
var s uint64
|
||||
go func() {
|
||||
x = 2
|
||||
atomic.StoreUint64(&s, 1)
|
||||
}()
|
||||
for !atomic.CompareAndSwapUint64(&s, 1, 0) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
x = 1
|
||||
}
|
||||
|
||||
func TestRaceAtomicStoreLoad(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
var a uint64
|
||||
go func() {
|
||||
atomic.StoreUint64(&a, 1)
|
||||
c <- true
|
||||
}()
|
||||
_ = a
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceAtomicLoadStore(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
var a uint64
|
||||
go func() {
|
||||
_ = atomic.LoadUint64(&a)
|
||||
c <- true
|
||||
}()
|
||||
a = 1
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceAtomicAddLoad(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
var a uint64
|
||||
go func() {
|
||||
atomic.AddUint64(&a, 1)
|
||||
c <- true
|
||||
}()
|
||||
_ = a
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceAtomicAddStore(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
var a uint64
|
||||
go func() {
|
||||
atomic.AddUint64(&a, 1)
|
||||
c <- true
|
||||
}()
|
||||
a = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
// A nil pointer in an atomic operation should not deadlock
|
||||
// the rest of the program. Used to hang indefinitely.
|
||||
func TestNoRaceAtomicCrash(t *testing.T) {
|
||||
var mutex sync.Mutex
|
||||
var nilptr *int32
|
||||
panics := 0
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
mutex.Lock()
|
||||
panics++
|
||||
mutex.Unlock()
|
||||
} else {
|
||||
panic("no panic")
|
||||
}
|
||||
}()
|
||||
atomic.AddInt32(nilptr, 1)
|
||||
}
|
||||
|
||||
func TestNoRaceDeferAtomicStore(t *testing.T) {
|
||||
// Test that when an atomic function is deferred directly, the
|
||||
// GC scans it correctly. See issue 42599.
|
||||
type foo struct {
|
||||
bar int64
|
||||
}
|
||||
|
||||
var doFork func(f *foo, depth int)
|
||||
doFork = func(f *foo, depth int) {
|
||||
atomic.StoreInt64(&f.bar, 1)
|
||||
defer atomic.StoreInt64(&f.bar, 0)
|
||||
if depth > 0 {
|
||||
for i := 0; i < 2; i++ {
|
||||
f2 := &foo{}
|
||||
go doFork(f2, depth-1)
|
||||
}
|
||||
}
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
f := &foo{}
|
||||
doFork(f, 11)
|
||||
}
|
||||
21
src/runtime/race/testdata/cgo_test.go
vendored
Normal file
21
src/runtime/race/testdata/cgo_test.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoRaceCgoSync(t *testing.T) {
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "run", "-race", "cgo_test_main.go")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Fatalf("program exited with error: %v\n", err)
|
||||
}
|
||||
}
|
||||
30
src/runtime/race/testdata/cgo_test_main.go
vendored
Normal file
30
src/runtime/race/testdata/cgo_test_main.go
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
int sync;
|
||||
|
||||
void Notify(void)
|
||||
{
|
||||
__sync_fetch_and_add(&sync, 1);
|
||||
}
|
||||
|
||||
void Wait(void)
|
||||
{
|
||||
while(__sync_fetch_and_add(&sync, 0) == 0) {}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
data := 0
|
||||
go func() {
|
||||
data = 1
|
||||
C.Notify()
|
||||
}()
|
||||
C.Wait()
|
||||
_ = data
|
||||
}
|
||||
787
src/runtime/race/testdata/chan_test.go
vendored
Normal file
787
src/runtime/race/testdata/chan_test.go
vendored
Normal file
@@ -0,0 +1,787 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceChanSync(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}()
|
||||
<-c
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestNoRaceChanSyncRev(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
c <- 0
|
||||
v = 2
|
||||
}()
|
||||
v = 1
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceChanAsync(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}()
|
||||
<-c
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanAsyncRev(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
c <- 0
|
||||
v = 1
|
||||
}()
|
||||
v = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceChanAsyncCloseRecv(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
func() {
|
||||
defer func() {
|
||||
recover()
|
||||
v = 2
|
||||
}()
|
||||
<-c
|
||||
}()
|
||||
}
|
||||
|
||||
func TestNoRaceChanAsyncCloseRecv2(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
_, _ = <-c
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestNoRaceChanAsyncCloseRecv3(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
for range c {
|
||||
}
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestNoRaceChanSyncCloseRecv(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
func() {
|
||||
defer func() {
|
||||
recover()
|
||||
v = 2
|
||||
}()
|
||||
<-c
|
||||
}()
|
||||
}
|
||||
|
||||
func TestNoRaceChanSyncCloseRecv2(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
_, _ = <-c
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestNoRaceChanSyncCloseRecv3(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
for range c {
|
||||
}
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanSyncCloseSend(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
func() {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
c <- 0
|
||||
}()
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanAsyncCloseSend(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
close(c)
|
||||
}()
|
||||
func() {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
for {
|
||||
c <- 0
|
||||
}
|
||||
}()
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanCloseClose(t *testing.T) {
|
||||
compl := make(chan bool, 2)
|
||||
v1 := 0
|
||||
v2 := 0
|
||||
_ = v1 + v2
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
v2 = 2
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
v1 = 1
|
||||
close(c)
|
||||
}()
|
||||
go func() {
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
v1 = 2
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
v2 = 1
|
||||
close(c)
|
||||
}()
|
||||
<-compl
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanSendLen(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
v = 1
|
||||
c <- 1
|
||||
}()
|
||||
for len(c) == 0 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanRecvLen(t *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int, 10)
|
||||
c <- 1
|
||||
go func() {
|
||||
v = 1
|
||||
<-c
|
||||
}()
|
||||
for len(c) != 0 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
v = 2
|
||||
}
|
||||
|
||||
func TestRaceChanSendSend(t *testing.T) {
|
||||
compl := make(chan bool, 2)
|
||||
v1 := 0
|
||||
v2 := 0
|
||||
_ = v1 + v2
|
||||
c := make(chan int, 1)
|
||||
go func() {
|
||||
v1 = 1
|
||||
select {
|
||||
case c <- 1:
|
||||
default:
|
||||
v2 = 2
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
go func() {
|
||||
v2 = 1
|
||||
select {
|
||||
case c <- 1:
|
||||
default:
|
||||
v1 = 2
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
<-compl
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceChanPtr(t *testing.T) {
|
||||
type msg struct {
|
||||
x int
|
||||
}
|
||||
c := make(chan *msg)
|
||||
go func() {
|
||||
c <- &msg{1}
|
||||
}()
|
||||
m := <-c
|
||||
m.x = 2
|
||||
}
|
||||
|
||||
func TestRaceChanWrongSend(t *testing.T) {
|
||||
v1 := 0
|
||||
v2 := 0
|
||||
_ = v1 + v2
|
||||
c := make(chan int, 2)
|
||||
go func() {
|
||||
v1 = 1
|
||||
c <- 1
|
||||
}()
|
||||
go func() {
|
||||
v2 = 2
|
||||
c <- 2
|
||||
}()
|
||||
time.Sleep(1e7)
|
||||
if <-c == 1 {
|
||||
v2 = 3
|
||||
} else {
|
||||
v1 = 3
|
||||
}
|
||||
}
|
||||
|
||||
func TestRaceChanWrongClose(t *testing.T) {
|
||||
v1 := 0
|
||||
v2 := 0
|
||||
_ = v1 + v2
|
||||
c := make(chan int, 1)
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
v1 = 1
|
||||
c <- 1
|
||||
done <- true
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(1e7)
|
||||
v2 = 2
|
||||
close(c)
|
||||
done <- true
|
||||
}()
|
||||
time.Sleep(2e7)
|
||||
if _, who := <-c; who {
|
||||
v2 = 2
|
||||
} else {
|
||||
v1 = 2
|
||||
}
|
||||
<-done
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestRaceChanSendClose(t *testing.T) {
|
||||
compl := make(chan bool, 2)
|
||||
c := make(chan int, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
recover()
|
||||
compl <- true
|
||||
}()
|
||||
c <- 1
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
close(c)
|
||||
compl <- true
|
||||
}()
|
||||
<-compl
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanSendSelectClose(t *testing.T) {
|
||||
compl := make(chan bool, 2)
|
||||
c := make(chan int, 1)
|
||||
c1 := make(chan int)
|
||||
go func() {
|
||||
defer func() {
|
||||
recover()
|
||||
compl <- true
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
select {
|
||||
case c <- 1:
|
||||
case <-c1:
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
close(c)
|
||||
compl <- true
|
||||
}()
|
||||
<-compl
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceSelectReadWriteAsync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
c1 := make(chan int, 10)
|
||||
c2 := make(chan int, 10)
|
||||
c3 := make(chan int)
|
||||
c2 <- 1
|
||||
go func() {
|
||||
select {
|
||||
case c1 <- x: // read of x races with...
|
||||
case c3 <- 1:
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
select {
|
||||
case x = <-c2: // ... write to x here
|
||||
case c3 <- 1:
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestRaceSelectReadWriteSync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
c1 := make(chan int)
|
||||
c2 := make(chan int)
|
||||
c3 := make(chan int)
|
||||
// make c1 and c2 ready for communication
|
||||
go func() {
|
||||
<-c1
|
||||
}()
|
||||
go func() {
|
||||
c2 <- 1
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case c1 <- x: // read of x races with...
|
||||
case c3 <- 1:
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
select {
|
||||
case x = <-c2: // ... write to x here
|
||||
case c3 <- 1:
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestNoRaceSelectReadWriteAsync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := 0
|
||||
c1 := make(chan int)
|
||||
c2 := make(chan int)
|
||||
go func() {
|
||||
select {
|
||||
case c1 <- x: // read of x does not race with...
|
||||
case c2 <- 1:
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
select {
|
||||
case x = <-c1: // ... write to x here
|
||||
case c2 <- 1:
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestRaceChanReadWriteAsync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
c1 := make(chan int, 10)
|
||||
c2 := make(chan int, 10)
|
||||
c2 <- 10
|
||||
x := 0
|
||||
go func() {
|
||||
c1 <- x // read of x races with...
|
||||
done <- true
|
||||
}()
|
||||
x = <-c2 // ... write to x here
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestRaceChanReadWriteSync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
c1 := make(chan int)
|
||||
c2 := make(chan int)
|
||||
// make c1 and c2 ready for communication
|
||||
go func() {
|
||||
<-c1
|
||||
}()
|
||||
go func() {
|
||||
c2 <- 10
|
||||
}()
|
||||
x := 0
|
||||
go func() {
|
||||
c1 <- x // read of x races with...
|
||||
done <- true
|
||||
}()
|
||||
x = <-c2 // ... write to x here
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestNoRaceChanReadWriteAsync(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
c1 := make(chan int, 10)
|
||||
x := 0
|
||||
go func() {
|
||||
c1 <- x // read of x does not race with...
|
||||
done <- true
|
||||
}()
|
||||
x = <-c1 // ... write to x here
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestNoRaceProducerConsumerUnbuffered(t *testing.T) {
|
||||
type Task struct {
|
||||
f func()
|
||||
done chan bool
|
||||
}
|
||||
|
||||
queue := make(chan Task)
|
||||
|
||||
go func() {
|
||||
t := <-queue
|
||||
t.f()
|
||||
t.done <- true
|
||||
}()
|
||||
|
||||
doit := func(f func()) {
|
||||
done := make(chan bool, 1)
|
||||
queue <- Task{f, done}
|
||||
<-done
|
||||
}
|
||||
|
||||
x := 0
|
||||
doit(func() {
|
||||
x = 1
|
||||
})
|
||||
_ = x
|
||||
}
|
||||
|
||||
func TestRaceChanItselfSend(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
c <- 0
|
||||
compl <- true
|
||||
}()
|
||||
c = make(chan int, 20)
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanItselfRecv(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int, 10)
|
||||
c <- 1
|
||||
go func() {
|
||||
<-c
|
||||
compl <- true
|
||||
}()
|
||||
time.Sleep(1e7)
|
||||
c = make(chan int, 20)
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanItselfNil(t *testing.T) {
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
c <- 0
|
||||
}()
|
||||
time.Sleep(1e7)
|
||||
c = nil
|
||||
_ = c
|
||||
}
|
||||
|
||||
func TestRaceChanItselfClose(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
close(c)
|
||||
compl <- true
|
||||
}()
|
||||
c = make(chan int)
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanItselfLen(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
_ = len(c)
|
||||
compl <- true
|
||||
}()
|
||||
c = make(chan int)
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceChanItselfCap(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
_ = cap(c)
|
||||
compl <- true
|
||||
}()
|
||||
c = make(chan int)
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceChanCloseLen(t *testing.T) {
|
||||
c := make(chan int, 10)
|
||||
r := make(chan int, 10)
|
||||
go func() {
|
||||
r <- len(c)
|
||||
}()
|
||||
go func() {
|
||||
close(c)
|
||||
r <- 0
|
||||
}()
|
||||
<-r
|
||||
<-r
|
||||
}
|
||||
|
||||
func TestNoRaceChanCloseCap(t *testing.T) {
|
||||
c := make(chan int, 10)
|
||||
r := make(chan int, 10)
|
||||
go func() {
|
||||
r <- cap(c)
|
||||
}()
|
||||
go func() {
|
||||
close(c)
|
||||
r <- 0
|
||||
}()
|
||||
<-r
|
||||
<-r
|
||||
}
|
||||
|
||||
func TestRaceChanCloseSend(t *testing.T) {
|
||||
compl := make(chan bool, 1)
|
||||
c := make(chan int, 10)
|
||||
go func() {
|
||||
close(c)
|
||||
compl <- true
|
||||
}()
|
||||
c <- 0
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceChanMutex(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
mtx := make(chan struct{}, 1)
|
||||
data := 0
|
||||
_ = data
|
||||
go func() {
|
||||
mtx <- struct{}{}
|
||||
data = 42
|
||||
<-mtx
|
||||
done <- struct{}{}
|
||||
}()
|
||||
mtx <- struct{}{}
|
||||
data = 43
|
||||
<-mtx
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestNoRaceSelectMutex(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
mtx := make(chan struct{}, 1)
|
||||
aux := make(chan bool)
|
||||
data := 0
|
||||
_ = data
|
||||
go func() {
|
||||
select {
|
||||
case mtx <- struct{}{}:
|
||||
case <-aux:
|
||||
}
|
||||
data = 42
|
||||
select {
|
||||
case <-mtx:
|
||||
case <-aux:
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case mtx <- struct{}{}:
|
||||
case <-aux:
|
||||
}
|
||||
data = 43
|
||||
select {
|
||||
case <-mtx:
|
||||
case <-aux:
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestRaceChanSem(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
mtx := make(chan bool, 2)
|
||||
data := 0
|
||||
_ = data
|
||||
go func() {
|
||||
mtx <- true
|
||||
data = 42
|
||||
<-mtx
|
||||
done <- struct{}{}
|
||||
}()
|
||||
mtx <- true
|
||||
data = 43
|
||||
<-mtx
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestNoRaceChanWaitGroup(t *testing.T) {
|
||||
const N = 10
|
||||
chanWg := make(chan bool, N/2)
|
||||
data := make([]int, N)
|
||||
for i := 0; i < N; i++ {
|
||||
chanWg <- true
|
||||
go func(i int) {
|
||||
data[i] = 42
|
||||
<-chanWg
|
||||
}(i)
|
||||
}
|
||||
for i := 0; i < cap(chanWg); i++ {
|
||||
chanWg <- true
|
||||
}
|
||||
for i := 0; i < N; i++ {
|
||||
_ = data[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Test that sender synchronizes with receiver even if the sender was blocked.
|
||||
func TestNoRaceBlockedSendSync(t *testing.T) {
|
||||
c := make(chan *int, 1)
|
||||
c <- nil
|
||||
go func() {
|
||||
i := 42
|
||||
c <- &i
|
||||
}()
|
||||
// Give the sender time to actually block.
|
||||
// This sleep is completely optional: race report must not be printed
|
||||
// regardless of whether the sender actually blocks or not.
|
||||
// It cannot lead to flakiness.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
<-c
|
||||
p := <-c
|
||||
if *p != 42 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
// The same as TestNoRaceBlockedSendSync above, but sender unblock happens in a select.
|
||||
func TestNoRaceBlockedSelectSendSync(t *testing.T) {
|
||||
c := make(chan *int, 1)
|
||||
c <- nil
|
||||
go func() {
|
||||
i := 42
|
||||
c <- &i
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
<-c
|
||||
select {
|
||||
case p := <-c:
|
||||
if *p != 42 {
|
||||
t.Fatal()
|
||||
}
|
||||
case <-make(chan int):
|
||||
}
|
||||
}
|
||||
|
||||
// Test that close synchronizes with a read from the empty closed channel.
|
||||
// See https://golang.org/issue/36714.
|
||||
func TestNoRaceCloseHappensBeforeRead(t *testing.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
var loc int
|
||||
var write = make(chan struct{})
|
||||
var read = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-write:
|
||||
_ = loc
|
||||
default:
|
||||
}
|
||||
close(read)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
loc = 1
|
||||
close(write)
|
||||
}()
|
||||
|
||||
<-read
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we call the proper race detector function when c.elemsize==0.
|
||||
// See https://github.com/golang/go/issues/42598
|
||||
func TestNoRaceElemSize0(t *testing.T) {
|
||||
var x, y int
|
||||
var c = make(chan struct{}, 2)
|
||||
c <- struct{}{}
|
||||
c <- struct{}{}
|
||||
go func() {
|
||||
x += 1
|
||||
<-c
|
||||
}()
|
||||
go func() {
|
||||
y += 1
|
||||
<-c
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
c <- struct{}{}
|
||||
c <- struct{}{}
|
||||
x += 1
|
||||
y += 1
|
||||
}
|
||||
186
src/runtime/race/testdata/comp_test.go
vendored
Normal file
186
src/runtime/race/testdata/comp_test.go
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type P struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
type S struct {
|
||||
s1, s2 P
|
||||
}
|
||||
|
||||
func TestNoRaceComp(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S
|
||||
go func() {
|
||||
s.s2.x = 1
|
||||
c <- true
|
||||
}()
|
||||
s.s2.y = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceComp2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S
|
||||
go func() {
|
||||
s.s1.x = 1
|
||||
c <- true
|
||||
}()
|
||||
s.s1.y = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceComp(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S
|
||||
go func() {
|
||||
s.s2.y = 1
|
||||
c <- true
|
||||
}()
|
||||
s.s2.y = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceComp2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S
|
||||
go func() {
|
||||
s.s1.x = 1
|
||||
c <- true
|
||||
}()
|
||||
s = S{}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceComp3(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S
|
||||
go func() {
|
||||
s.s2.y = 1
|
||||
c <- true
|
||||
}()
|
||||
s = S{}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceCompArray(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]S, 10)
|
||||
x := 4
|
||||
go func() {
|
||||
s[x].s2.y = 1
|
||||
c <- true
|
||||
}()
|
||||
x = 5
|
||||
<-c
|
||||
}
|
||||
|
||||
type P2 P
|
||||
type S2 S
|
||||
|
||||
func TestRaceConv1(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var p P2
|
||||
go func() {
|
||||
p.x = 1
|
||||
c <- true
|
||||
}()
|
||||
_ = P(p).x
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceConv2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var p P2
|
||||
go func() {
|
||||
p.x = 1
|
||||
c <- true
|
||||
}()
|
||||
ptr := &p
|
||||
_ = P(*ptr).x
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceConv3(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var s S2
|
||||
go func() {
|
||||
s.s1.x = 1
|
||||
c <- true
|
||||
}()
|
||||
_ = P2(S(s).s1).x
|
||||
<-c
|
||||
}
|
||||
|
||||
type X struct {
|
||||
V [4]P
|
||||
}
|
||||
|
||||
type X2 X
|
||||
|
||||
func TestRaceConv4(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var x X2
|
||||
go func() {
|
||||
x.V[1].x = 1
|
||||
c <- true
|
||||
}()
|
||||
_ = P2(X(x).V[1]).x
|
||||
<-c
|
||||
}
|
||||
|
||||
type Ptr struct {
|
||||
s1, s2 *P
|
||||
}
|
||||
|
||||
func TestNoRaceCompPtr(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
p := Ptr{&P{}, &P{}}
|
||||
go func() {
|
||||
p.s1.x = 1
|
||||
c <- true
|
||||
}()
|
||||
p.s1.y = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceCompPtr2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
p := Ptr{&P{}, &P{}}
|
||||
go func() {
|
||||
p.s1.x = 1
|
||||
c <- true
|
||||
}()
|
||||
_ = p
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceCompPtr(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
p := Ptr{&P{}, &P{}}
|
||||
go func() {
|
||||
p.s2.x = 1
|
||||
c <- true
|
||||
}()
|
||||
p.s2.x = 2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceCompPtr2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
p := Ptr{&P{}, &P{}}
|
||||
go func() {
|
||||
p.s2.x = 1
|
||||
c <- true
|
||||
}()
|
||||
p.s2 = &P{}
|
||||
<-c
|
||||
}
|
||||
68
src/runtime/race/testdata/finalizer_test.go
vendored
Normal file
68
src/runtime/race/testdata/finalizer_test.go
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceFin(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
x := new(string)
|
||||
runtime.SetFinalizer(x, func(x *string) {
|
||||
*x = "foo"
|
||||
})
|
||||
*x = "bar"
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
runtime.GC()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
var finVar struct {
|
||||
sync.Mutex
|
||||
cnt int
|
||||
}
|
||||
|
||||
func TestNoRaceFinGlobal(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
x := new(string)
|
||||
runtime.SetFinalizer(x, func(x *string) {
|
||||
finVar.Lock()
|
||||
finVar.cnt++
|
||||
finVar.Unlock()
|
||||
})
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
runtime.GC()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
finVar.Lock()
|
||||
finVar.cnt++
|
||||
finVar.Unlock()
|
||||
}
|
||||
|
||||
func TestRaceFin(t *testing.T) {
|
||||
c := make(chan bool)
|
||||
y := 0
|
||||
_ = y
|
||||
go func() {
|
||||
x := new(string)
|
||||
runtime.SetFinalizer(x, func(x *string) {
|
||||
y = 42
|
||||
})
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
runtime.GC()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
y = 66
|
||||
}
|
||||
75
src/runtime/race/testdata/io_test.go
vendored
Normal file
75
src/runtime/race/testdata/io_test.go
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceIOFile(t *testing.T) {
|
||||
x := 0
|
||||
path := t.TempDir()
|
||||
fname := filepath.Join(path, "data")
|
||||
go func() {
|
||||
x = 42
|
||||
f, _ := os.Create(fname)
|
||||
f.Write([]byte("done"))
|
||||
f.Close()
|
||||
}()
|
||||
for {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
time.Sleep(1e6)
|
||||
continue
|
||||
}
|
||||
buf := make([]byte, 100)
|
||||
count, err := f.Read(buf)
|
||||
if count == 0 {
|
||||
time.Sleep(1e6)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
_ = x
|
||||
}
|
||||
|
||||
var (
|
||||
regHandler sync.Once
|
||||
handlerData int
|
||||
)
|
||||
|
||||
func TestNoRaceIOHttp(t *testing.T) {
|
||||
regHandler.Do(func() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerData++
|
||||
fmt.Fprintf(w, "test")
|
||||
handlerData++
|
||||
})
|
||||
})
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
go http.Serve(ln, nil)
|
||||
handlerData++
|
||||
_, err = http.Get("http://" + ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("http.Get: %v", err)
|
||||
}
|
||||
handlerData++
|
||||
_, err = http.Get("http://" + ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("http.Get: %v", err)
|
||||
}
|
||||
handlerData++
|
||||
}
|
||||
20
src/runtime/race/testdata/issue12225_test.go
vendored
Normal file
20
src/runtime/race/testdata/issue12225_test.go
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// golang.org/issue/12225
|
||||
// The test is that this compiles at all.
|
||||
|
||||
//go:noinline
|
||||
func convert(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func issue12225() {
|
||||
println(*(*int)(unsafe.Pointer(&convert("")[0])))
|
||||
println(*(*int)(unsafe.Pointer(&[]byte("")[0])))
|
||||
}
|
||||
76
src/runtime/race/testdata/issue12664_test.go
vendored
Normal file
76
src/runtime/race/testdata/issue12664_test.go
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var issue12664 = "hi"
|
||||
|
||||
func TestRaceIssue12664(t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
issue12664 = "bye"
|
||||
close(c)
|
||||
}()
|
||||
fmt.Println(issue12664)
|
||||
<-c
|
||||
}
|
||||
|
||||
type MyI interface {
|
||||
foo()
|
||||
}
|
||||
|
||||
type MyT int
|
||||
|
||||
func (MyT) foo() {
|
||||
}
|
||||
|
||||
var issue12664_2 MyT = 0
|
||||
|
||||
func TestRaceIssue12664_2(t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
issue12664_2 = 1
|
||||
close(c)
|
||||
}()
|
||||
func(x MyI) {
|
||||
// Never true, but prevents inlining.
|
||||
if x.(MyT) == -1 {
|
||||
close(c)
|
||||
}
|
||||
}(issue12664_2)
|
||||
<-c
|
||||
}
|
||||
|
||||
var issue12664_3 MyT = 0
|
||||
|
||||
func TestRaceIssue12664_3(t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
issue12664_3 = 1
|
||||
close(c)
|
||||
}()
|
||||
var r MyT
|
||||
var i any = r
|
||||
issue12664_3 = i.(MyT)
|
||||
<-c
|
||||
}
|
||||
|
||||
var issue12664_4 MyT = 0
|
||||
|
||||
func TestRaceIssue12664_4(t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
issue12664_4 = 1
|
||||
close(c)
|
||||
}()
|
||||
var r MyT
|
||||
var i MyI = r
|
||||
issue12664_4 = i.(MyT)
|
||||
<-c
|
||||
}
|
||||
13
src/runtime/race/testdata/issue13264_test.go
vendored
Normal file
13
src/runtime/race/testdata/issue13264_test.go
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
// golang.org/issue/13264
|
||||
// The test is that this compiles at all.
|
||||
|
||||
func issue13264() {
|
||||
for ; ; []map[int]int{}[0][0] = 0 {
|
||||
}
|
||||
}
|
||||
335
src/runtime/race/testdata/map_test.go
vendored
Normal file
335
src/runtime/race/testdata/map_test.go
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRaceMapRW(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_ = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
m[1] = 1
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapRW2(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_, _ = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
m[1] = 1
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapRWArray(t *testing.T) {
|
||||
// Check instrumentation of unaddressable arrays (issue 4578).
|
||||
m := make(map[int][2]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_ = m[1][1]
|
||||
ch <- true
|
||||
}()
|
||||
m[2] = [2]int{1, 2}
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceMapRR(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_, _ = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
_ = m[1]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapRange(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
for range m {
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
m[1] = 1
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapRange2(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
for range m {
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
m[1] = 1
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceMapRangeRange(t *testing.T) {
|
||||
m := make(map[int]int)
|
||||
// now the map is not empty and range triggers an event
|
||||
// should work without this (as in other tests)
|
||||
// so it is suspicious if this test passes and others don't
|
||||
m[0] = 0
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
for range m {
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
for range m {
|
||||
}
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapLen(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_ = len(m)
|
||||
ch <- true
|
||||
}()
|
||||
m[""] = true
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapDelete(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
delete(m, "")
|
||||
ch <- true
|
||||
}()
|
||||
m[""] = true
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapLenDelete(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
delete(m, "a")
|
||||
ch <- true
|
||||
}()
|
||||
_ = len(m)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapVariable(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
m := make(map[int]int)
|
||||
_ = m
|
||||
go func() {
|
||||
m = make(map[int]int)
|
||||
ch <- true
|
||||
}()
|
||||
m = make(map[int]int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapVariable2(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
m := make(map[int]int)
|
||||
go func() {
|
||||
m[1] = 1
|
||||
ch <- true
|
||||
}()
|
||||
m = make(map[int]int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapVariable3(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
m := make(map[int]int)
|
||||
go func() {
|
||||
_ = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
m = make(map[int]int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
type Big struct {
|
||||
x [17]int32
|
||||
}
|
||||
|
||||
func TestRaceMapLookupPartKey(t *testing.T) {
|
||||
k := &Big{}
|
||||
m := make(map[Big]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
k.x[8] = 1
|
||||
ch <- true
|
||||
}()
|
||||
_ = m[*k]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapLookupPartKey2(t *testing.T) {
|
||||
k := &Big{}
|
||||
m := make(map[Big]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
k.x[8] = 1
|
||||
ch <- true
|
||||
}()
|
||||
_, _ = m[*k]
|
||||
<-ch
|
||||
}
|
||||
func TestRaceMapDeletePartKey(t *testing.T) {
|
||||
k := &Big{}
|
||||
m := make(map[Big]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
k.x[8] = 1
|
||||
ch <- true
|
||||
}()
|
||||
delete(m, *k)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapInsertPartKey(t *testing.T) {
|
||||
k := &Big{}
|
||||
m := make(map[Big]bool)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
k.x[8] = 1
|
||||
ch <- true
|
||||
}()
|
||||
m[*k] = true
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapInsertPartVal(t *testing.T) {
|
||||
v := &Big{}
|
||||
m := make(map[int]Big)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
v.x[8] = 1
|
||||
ch <- true
|
||||
}()
|
||||
m[1] = *v
|
||||
<-ch
|
||||
}
|
||||
|
||||
// Test for issue 7561.
|
||||
func TestRaceMapAssignMultipleReturn(t *testing.T) {
|
||||
connect := func() (int, error) { return 42, nil }
|
||||
conns := make(map[int][]int)
|
||||
conns[1] = []int{0}
|
||||
ch := make(chan bool, 1)
|
||||
var err error
|
||||
_ = err
|
||||
go func() {
|
||||
conns[1][0], err = connect()
|
||||
ch <- true
|
||||
}()
|
||||
x := conns[1][0]
|
||||
_ = x
|
||||
<-ch
|
||||
}
|
||||
|
||||
// BigKey and BigVal must be larger than 256 bytes,
|
||||
// so that compiler sets KindGCProg for them.
|
||||
type BigKey [1000]*int
|
||||
|
||||
type BigVal struct {
|
||||
x int
|
||||
y [1000]*int
|
||||
}
|
||||
|
||||
func TestRaceMapBigKeyAccess1(t *testing.T) {
|
||||
m := make(map[BigKey]int)
|
||||
var k BigKey
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_ = m[k]
|
||||
ch <- true
|
||||
}()
|
||||
k[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigKeyAccess2(t *testing.T) {
|
||||
m := make(map[BigKey]int)
|
||||
var k BigKey
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
_, _ = m[k]
|
||||
ch <- true
|
||||
}()
|
||||
k[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigKeyInsert(t *testing.T) {
|
||||
m := make(map[BigKey]int)
|
||||
var k BigKey
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
m[k] = 1
|
||||
ch <- true
|
||||
}()
|
||||
k[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigKeyDelete(t *testing.T) {
|
||||
m := make(map[BigKey]int)
|
||||
var k BigKey
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
delete(m, k)
|
||||
ch <- true
|
||||
}()
|
||||
k[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigValInsert(t *testing.T) {
|
||||
m := make(map[int]BigVal)
|
||||
var v BigVal
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
m[1] = v
|
||||
ch <- true
|
||||
}()
|
||||
v.y[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigValAccess1(t *testing.T) {
|
||||
m := make(map[int]BigVal)
|
||||
var v BigVal
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
v = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
v.y[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMapBigValAccess2(t *testing.T) {
|
||||
m := make(map[int]BigVal)
|
||||
var v BigVal
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
v, _ = m[1]
|
||||
ch <- true
|
||||
}()
|
||||
v.y[30] = new(int)
|
||||
<-ch
|
||||
}
|
||||
2132
src/runtime/race/testdata/mop_test.go
vendored
Normal file
2132
src/runtime/race/testdata/mop_test.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
150
src/runtime/race/testdata/mutex_test.go
vendored
Normal file
150
src/runtime/race/testdata/mutex_test.go
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceMutex(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
var x int16 = 0
|
||||
_ = x
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
x = 1
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.Lock()
|
||||
x = 2
|
||||
mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMutex(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
var x int16 = 0
|
||||
_ = x
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x = 1
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
x = 2
|
||||
mu.Lock()
|
||||
mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceMutex2(t *testing.T) {
|
||||
var mu1 sync.Mutex
|
||||
var mu2 sync.Mutex
|
||||
var x int8 = 0
|
||||
_ = x
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
mu1.Lock()
|
||||
defer mu1.Unlock()
|
||||
x = 1
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu2.Lock()
|
||||
x = 2
|
||||
mu2.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceMutexPureHappensBefore(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
var x int16 = 0
|
||||
_ = x
|
||||
written := false
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
x = 1
|
||||
mu.Lock()
|
||||
written = true
|
||||
mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
mu.Lock()
|
||||
for !written {
|
||||
mu.Unlock()
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
mu.Lock()
|
||||
}
|
||||
mu.Unlock()
|
||||
x = 1
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceMutexSemaphore(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
ch := make(chan bool, 2)
|
||||
x := 0
|
||||
_ = x
|
||||
mu.Lock()
|
||||
go func() {
|
||||
x = 1
|
||||
mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.Lock()
|
||||
x = 2
|
||||
mu.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
// from doc/go_mem.html
|
||||
func TestNoRaceMutexExampleFromHtml(t *testing.T) {
|
||||
var l sync.Mutex
|
||||
a := ""
|
||||
|
||||
l.Lock()
|
||||
go func() {
|
||||
a = "hello, world"
|
||||
l.Unlock()
|
||||
}()
|
||||
l.Lock()
|
||||
_ = a
|
||||
}
|
||||
|
||||
func TestRaceMutexOverwrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
var mu sync.Mutex
|
||||
go func() {
|
||||
mu = sync.Mutex{}
|
||||
c <- true
|
||||
}()
|
||||
mu.Lock()
|
||||
<-c
|
||||
}
|
||||
47
src/runtime/race/testdata/pool_test.go
vendored
Normal file
47
src/runtime/race/testdata/pool_test.go
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRacePool(t *testing.T) {
|
||||
// Pool randomly drops the argument on the floor during Put.
|
||||
// Repeat so that at least one iteration gets reuse.
|
||||
for i := 0; i < 10; i++ {
|
||||
c := make(chan int)
|
||||
p := &sync.Pool{New: func() any { return make([]byte, 10) }}
|
||||
x := p.Get().([]byte)
|
||||
x[0] = 1
|
||||
p.Put(x)
|
||||
go func() {
|
||||
y := p.Get().([]byte)
|
||||
y[0] = 2
|
||||
c <- 1
|
||||
}()
|
||||
x[0] = 3
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRacePool(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
p := &sync.Pool{New: func() any { return make([]byte, 10) }}
|
||||
x := p.Get().([]byte)
|
||||
x[0] = 1
|
||||
p.Put(x)
|
||||
go func() {
|
||||
y := p.Get().([]byte)
|
||||
y[0] = 2
|
||||
p.Put(y)
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
x = p.Get().([]byte)
|
||||
x[0] = 3
|
||||
}
|
||||
}
|
||||
77
src/runtime/race/testdata/rangefunc_test.go
vendored
Normal file
77
src/runtime/race/testdata/rangefunc_test.go
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build goexperiment.rangefunc
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Seq2[T1, T2 any] func(yield func(T1, T2) bool)
|
||||
|
||||
// ofSliceIndex returns a Seq over the elements of s. It is equivalent
|
||||
// to range s, except that it splits s into two halves and iterates
|
||||
// in two separate goroutines. This is racy if yield is racy, and yield
|
||||
// will be racy if it contains an early exit.
|
||||
func ofSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
|
||||
return func(yield func(int, T) bool) {
|
||||
c := make(chan bool, 2)
|
||||
var done atomic.Bool
|
||||
go func() {
|
||||
for i := 0; i < len(s)/2; i++ {
|
||||
if !done.Load() && !yield(i, s[i]) {
|
||||
done.Store(true)
|
||||
c <- false
|
||||
}
|
||||
}
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
for i := len(s) / 2; i < len(s); i++ {
|
||||
if !done.Load() && !yield(i, s[i]) {
|
||||
done.Store(true)
|
||||
c <- false
|
||||
}
|
||||
}
|
||||
c <- true
|
||||
return
|
||||
}()
|
||||
if !<-c {
|
||||
return
|
||||
}
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
// foo is racy, or not, depending on the value of v
|
||||
// (0-4 == racy, otherwise, not racy).
|
||||
func foo(v int) int64 {
|
||||
var asum atomic.Int64
|
||||
for i, x := range ofSliceIndex([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
|
||||
if i%5 == v {
|
||||
break
|
||||
}
|
||||
asum.Add(x) // don't race on asum
|
||||
runtime.Gosched()
|
||||
}
|
||||
return 100 + asum.Load()
|
||||
}
|
||||
|
||||
// TestRaceRangeFuncIterator races because x%5 can be equal to 4,
|
||||
// therefore foo can early exit.
|
||||
func TestRaceRangeFuncIterator(t *testing.T) {
|
||||
x := foo(4)
|
||||
t.Logf("foo(4)=%d", x)
|
||||
}
|
||||
|
||||
// TestNoRaceRangeFuncIterator does not race because x%5 is never 5,
|
||||
// therefore foo's loop will not exit early, and this it will not race.
|
||||
func TestNoRaceRangeFuncIterator(t *testing.T) {
|
||||
x := foo(5)
|
||||
t.Logf("foo(5)=%d", x)
|
||||
}
|
||||
46
src/runtime/race/testdata/reflect_test.go
vendored
Normal file
46
src/runtime/race/testdata/reflect_test.go
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRaceReflectRW(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
i := 0
|
||||
v := reflect.ValueOf(&i)
|
||||
go func() {
|
||||
v.Elem().Set(reflect.ValueOf(1))
|
||||
ch <- true
|
||||
}()
|
||||
_ = v.Elem().Int()
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceReflectWW(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
i := 0
|
||||
v := reflect.ValueOf(&i)
|
||||
go func() {
|
||||
v.Elem().Set(reflect.ValueOf(1))
|
||||
ch <- true
|
||||
}()
|
||||
v.Elem().Set(reflect.ValueOf(2))
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceReflectCopyWW(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]byte, 2)
|
||||
v := reflect.ValueOf(a)
|
||||
go func() {
|
||||
reflect.Copy(v, v)
|
||||
ch <- true
|
||||
}()
|
||||
reflect.Copy(v, v)
|
||||
<-ch
|
||||
}
|
||||
189
src/runtime/race/testdata/regression_test.go
vendored
Normal file
189
src/runtime/race/testdata/regression_test.go
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code patterns that caused problems in the past.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type LogImpl struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func NewLog() (l LogImpl) {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
_ = l
|
||||
c <- true
|
||||
}()
|
||||
l = LogImpl{}
|
||||
<-c
|
||||
return
|
||||
}
|
||||
|
||||
var _ LogImpl = NewLog()
|
||||
|
||||
func MakeMap() map[int]int {
|
||||
return make(map[int]int)
|
||||
}
|
||||
|
||||
func InstrumentMapLen() {
|
||||
_ = len(MakeMap())
|
||||
}
|
||||
|
||||
func InstrumentMapLen2() {
|
||||
m := make(map[int]map[int]int)
|
||||
_ = len(m[0])
|
||||
}
|
||||
|
||||
func InstrumentMapLen3() {
|
||||
m := make(map[int]*map[int]int)
|
||||
_ = len(*m[0])
|
||||
}
|
||||
|
||||
func TestRaceUnaddressableMapLen(t *testing.T) {
|
||||
m := make(map[int]map[int]int)
|
||||
ch := make(chan int, 1)
|
||||
m[0] = make(map[int]int)
|
||||
go func() {
|
||||
_ = len(m[0])
|
||||
ch <- 0
|
||||
}()
|
||||
m[0][0] = 1
|
||||
<-ch
|
||||
}
|
||||
|
||||
type Rect struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
min, max Rect
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func NewImage() Image {
|
||||
return Image{}
|
||||
}
|
||||
|
||||
func AddrOfTemp() {
|
||||
_ = NewImage().min
|
||||
}
|
||||
|
||||
type TypeID int
|
||||
|
||||
func (t *TypeID) encodeType(x int) (tt TypeID, err error) {
|
||||
switch x {
|
||||
case 0:
|
||||
return t.encodeType(x * x)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type stack []int
|
||||
|
||||
func (s *stack) push(x int) {
|
||||
*s = append(*s, x)
|
||||
}
|
||||
|
||||
func (s *stack) pop() int {
|
||||
i := len(*s)
|
||||
n := (*s)[i-1]
|
||||
*s = (*s)[:i-1]
|
||||
return n
|
||||
}
|
||||
|
||||
func TestNoRaceStackPushPop(t *testing.T) {
|
||||
var s stack
|
||||
go func(s *stack) {}(&s)
|
||||
s.push(1)
|
||||
x := s.pop()
|
||||
_ = x
|
||||
}
|
||||
|
||||
type RpcChan struct {
|
||||
c chan bool
|
||||
}
|
||||
|
||||
var makeChanCalls int
|
||||
|
||||
//go:noinline
|
||||
func makeChan() *RpcChan {
|
||||
makeChanCalls++
|
||||
c := &RpcChan{make(chan bool, 1)}
|
||||
c.c <- true
|
||||
return c
|
||||
}
|
||||
|
||||
func call() bool {
|
||||
x := <-makeChan().c
|
||||
return x
|
||||
}
|
||||
|
||||
func TestNoRaceRpcChan(t *testing.T) {
|
||||
makeChanCalls = 0
|
||||
_ = call()
|
||||
if makeChanCalls != 1 {
|
||||
t.Fatalf("makeChanCalls %d, expected 1\n", makeChanCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func divInSlice() {
|
||||
v := make([]int64, 10)
|
||||
i := 1
|
||||
_ = v[(i*4)/3]
|
||||
}
|
||||
|
||||
func TestNoRaceReturn(t *testing.T) {
|
||||
c := make(chan int)
|
||||
noRaceReturn(c)
|
||||
<-c
|
||||
}
|
||||
|
||||
// Return used to do an implicit a = a, causing a read/write race
|
||||
// with the goroutine. Compiler has an optimization to avoid that now.
|
||||
// See issue 4014.
|
||||
func noRaceReturn(c chan int) (a, b int) {
|
||||
a = 42
|
||||
go func() {
|
||||
_ = a
|
||||
c <- 1
|
||||
}()
|
||||
return a, 10
|
||||
}
|
||||
|
||||
func issue5431() {
|
||||
var p **inltype
|
||||
if inlinetest(p).x && inlinetest(p).y {
|
||||
} else if inlinetest(p).x || inlinetest(p).y {
|
||||
}
|
||||
}
|
||||
|
||||
type inltype struct {
|
||||
x, y bool
|
||||
}
|
||||
|
||||
func inlinetest(p **inltype) *inltype {
|
||||
return *p
|
||||
}
|
||||
|
||||
type iface interface {
|
||||
Foo() *struct{ b bool }
|
||||
}
|
||||
|
||||
type Int int
|
||||
|
||||
func (i Int) Foo() *struct{ b bool } {
|
||||
return &struct{ b bool }{false}
|
||||
}
|
||||
|
||||
func TestNoRaceForInfiniteLoop(t *testing.T) {
|
||||
var x Int
|
||||
// interface conversion causes nodes to be put on init list
|
||||
for iface(x).Foo().b {
|
||||
}
|
||||
}
|
||||
154
src/runtime/race/testdata/rwmutex_test.go
vendored
Normal file
154
src/runtime/race/testdata/rwmutex_test.go
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRaceMutexRWMutex(t *testing.T) {
|
||||
var mu1 sync.Mutex
|
||||
var mu2 sync.RWMutex
|
||||
var x int16 = 0
|
||||
_ = x
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
mu1.Lock()
|
||||
defer mu1.Unlock()
|
||||
x = 1
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu2.Lock()
|
||||
x = 2
|
||||
mu2.Unlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceRWMutex(t *testing.T) {
|
||||
var mu sync.RWMutex
|
||||
var x, y int64 = 0, 1
|
||||
_ = y
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
x = 2
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y = x
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceRWMutexMultipleReaders(t *testing.T) {
|
||||
var mu sync.RWMutex
|
||||
var x, y int64 = 0, 1
|
||||
ch := make(chan bool, 4)
|
||||
go func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
x = 2
|
||||
ch <- true
|
||||
}()
|
||||
// Use three readers so that no matter what order they're
|
||||
// scheduled in, two will be on the same side of the write
|
||||
// lock above.
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y = x + 1
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y = x + 2
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y = x + 3
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
<-ch
|
||||
<-ch
|
||||
_ = y
|
||||
}
|
||||
|
||||
func TestNoRaceRWMutexMultipleReaders(t *testing.T) {
|
||||
var mu sync.RWMutex
|
||||
x := int64(0)
|
||||
ch := make(chan bool, 4)
|
||||
go func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
x = 2
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y := x + 1
|
||||
_ = y
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y := x + 2
|
||||
_ = y
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
mu.RLock()
|
||||
y := x + 3
|
||||
_ = y
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
<-ch
|
||||
<-ch
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceRWMutexTransitive(t *testing.T) {
|
||||
var mu sync.RWMutex
|
||||
x := int64(0)
|
||||
ch := make(chan bool, 2)
|
||||
go func() {
|
||||
mu.RLock()
|
||||
_ = x
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(1e7)
|
||||
mu.RLock()
|
||||
_ = x
|
||||
mu.RUnlock()
|
||||
ch <- true
|
||||
}()
|
||||
time.Sleep(2e7)
|
||||
mu.Lock()
|
||||
x = 42
|
||||
mu.Unlock()
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
293
src/runtime/race/testdata/select_test.go
vendored
Normal file
293
src/runtime/race/testdata/select_test.go
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoRaceSelect1(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool)
|
||||
c := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
|
||||
go func() {
|
||||
x = 1
|
||||
// At least two channels are needed because
|
||||
// otherwise the compiler optimizes select out.
|
||||
// See comment in runtime/select.go:^func selectgo.
|
||||
select {
|
||||
case c <- true:
|
||||
case c1 <- true:
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
case c1 <- true:
|
||||
}
|
||||
x = 2
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceSelect2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool)
|
||||
c := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-c:
|
||||
case <-c1:
|
||||
}
|
||||
x = 1
|
||||
compl <- true
|
||||
}()
|
||||
x = 2
|
||||
close(c)
|
||||
runtime.Gosched()
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceSelect3(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool)
|
||||
c := make(chan bool, 10)
|
||||
c1 := make(chan bool)
|
||||
go func() {
|
||||
x = 1
|
||||
select {
|
||||
case c <- true:
|
||||
case <-c1:
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
<-c
|
||||
x = 2
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestNoRaceSelect4(t *testing.T) {
|
||||
type Task struct {
|
||||
f func()
|
||||
done chan bool
|
||||
}
|
||||
|
||||
queue := make(chan Task)
|
||||
dummy := make(chan bool)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case t := <-queue:
|
||||
t.f()
|
||||
t.done <- true
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
doit := func(f func()) {
|
||||
done := make(chan bool, 1)
|
||||
select {
|
||||
case queue <- Task{f, done}:
|
||||
case <-dummy:
|
||||
}
|
||||
select {
|
||||
case <-done:
|
||||
case <-dummy:
|
||||
}
|
||||
}
|
||||
|
||||
var x int
|
||||
doit(func() {
|
||||
x = 1
|
||||
})
|
||||
_ = x
|
||||
}
|
||||
|
||||
func TestNoRaceSelect5(t *testing.T) {
|
||||
test := func(sel, needSched bool) {
|
||||
var x int
|
||||
_ = x
|
||||
ch := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
|
||||
done := make(chan bool, 2)
|
||||
go func() {
|
||||
if needSched {
|
||||
runtime.Gosched()
|
||||
}
|
||||
// println(1)
|
||||
x = 1
|
||||
if sel {
|
||||
select {
|
||||
case ch <- true:
|
||||
case <-c1:
|
||||
}
|
||||
} else {
|
||||
ch <- true
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
|
||||
go func() {
|
||||
// println(2)
|
||||
if sel {
|
||||
select {
|
||||
case <-ch:
|
||||
case <-c1:
|
||||
}
|
||||
} else {
|
||||
<-ch
|
||||
}
|
||||
x = 1
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
<-done
|
||||
}
|
||||
|
||||
test(true, true)
|
||||
test(true, false)
|
||||
test(false, true)
|
||||
test(false, false)
|
||||
}
|
||||
|
||||
func TestRaceSelect1(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool, 2)
|
||||
c := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
|
||||
go func() {
|
||||
<-c
|
||||
<-c
|
||||
}()
|
||||
f := func() {
|
||||
select {
|
||||
case c <- true:
|
||||
case c1 <- true:
|
||||
}
|
||||
x = 1
|
||||
compl <- true
|
||||
}
|
||||
go f()
|
||||
go f()
|
||||
<-compl
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceSelect2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool)
|
||||
c := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
go func() {
|
||||
x = 1
|
||||
select {
|
||||
case <-c:
|
||||
case <-c1:
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
close(c)
|
||||
x = 2
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceSelect3(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
compl := make(chan bool)
|
||||
c := make(chan bool)
|
||||
c1 := make(chan bool)
|
||||
go func() {
|
||||
x = 1
|
||||
select {
|
||||
case c <- true:
|
||||
case c1 <- true:
|
||||
}
|
||||
compl <- true
|
||||
}()
|
||||
x = 2
|
||||
select {
|
||||
case <-c:
|
||||
}
|
||||
<-compl
|
||||
}
|
||||
|
||||
func TestRaceSelect4(t *testing.T) {
|
||||
done := make(chan bool, 1)
|
||||
var x int
|
||||
go func() {
|
||||
select {
|
||||
default:
|
||||
x = 2
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
_ = x
|
||||
<-done
|
||||
}
|
||||
|
||||
// The idea behind this test:
|
||||
// there are two variables, access to one
|
||||
// of them is synchronized, access to the other
|
||||
// is not.
|
||||
// Select must (unconditionally) choose the non-synchronized variable
|
||||
// thus causing exactly one race.
|
||||
// Currently this test doesn't look like it accomplishes
|
||||
// this goal.
|
||||
func TestRaceSelect5(t *testing.T) {
|
||||
done := make(chan bool, 1)
|
||||
c1 := make(chan bool, 1)
|
||||
c2 := make(chan bool)
|
||||
var x, y int
|
||||
go func() {
|
||||
select {
|
||||
case c1 <- true:
|
||||
x = 1
|
||||
case c2 <- true:
|
||||
y = 1
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
_ = x
|
||||
_ = y
|
||||
<-done
|
||||
}
|
||||
|
||||
// select statements may introduce
|
||||
// flakiness: whether this test contains
|
||||
// a race depends on the scheduling
|
||||
// (some may argue that the code contains
|
||||
// this race by definition)
|
||||
/*
|
||||
func TestFlakyDefault(t *testing.T) {
|
||||
var x int
|
||||
c := make(chan bool, 1)
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
select {
|
||||
case <-c:
|
||||
x = 2
|
||||
default:
|
||||
x = 3
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
x = 1
|
||||
c <- true
|
||||
_ = x
|
||||
<-done
|
||||
}
|
||||
*/
|
||||
608
src/runtime/race/testdata/slice_test.go
vendored
Normal file
608
src/runtime/race/testdata/slice_test.go
vendored
Normal file
@@ -0,0 +1,608 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRaceSliceRW(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 2)
|
||||
go func() {
|
||||
a[1] = 1
|
||||
ch <- true
|
||||
}()
|
||||
_ = a[1]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceRW(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 2)
|
||||
go func() {
|
||||
a[0] = 1
|
||||
ch <- true
|
||||
}()
|
||||
_ = a[1]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceWW(t *testing.T) {
|
||||
a := make([]int, 10)
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
a[1] = 1
|
||||
ch <- true
|
||||
}()
|
||||
a[1] = 2
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceArrayWW(t *testing.T) {
|
||||
var a [5]int
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
a[0] = 1
|
||||
ch <- true
|
||||
}()
|
||||
a[1] = 2
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceArrayWW(t *testing.T) {
|
||||
var a [5]int
|
||||
ch := make(chan bool, 1)
|
||||
go func() {
|
||||
a[1] = 1
|
||||
ch <- true
|
||||
}()
|
||||
a[1] = 2
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceWriteLen(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]bool, 1)
|
||||
go func() {
|
||||
a[0] = true
|
||||
ch <- true
|
||||
}()
|
||||
_ = len(a)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceWriteCap(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]uint64, 100)
|
||||
go func() {
|
||||
a[50] = 123
|
||||
ch <- true
|
||||
}()
|
||||
_ = cap(a)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceCopyRead(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 10)
|
||||
b := make([]int, 10)
|
||||
go func() {
|
||||
_ = a[5]
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceWriteCopy(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 10)
|
||||
b := make([]int, 10)
|
||||
go func() {
|
||||
a[5] = 1
|
||||
ch <- true
|
||||
}()
|
||||
copy(a[:5], b[:5])
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceCopyWrite2(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 10)
|
||||
b := make([]int, 10)
|
||||
go func() {
|
||||
b[5] = 1
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceCopyWrite3(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]byte, 10)
|
||||
go func() {
|
||||
a[7] = 1
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, "qwertyqwerty")
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceCopyRead(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]int, 10)
|
||||
b := make([]int, 10)
|
||||
go func() {
|
||||
_ = b[5]
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRacePointerSliceCopyRead(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]*int, 10)
|
||||
b := make([]*int, 10)
|
||||
go func() {
|
||||
_ = a[5]
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRacePointerSliceWriteCopy(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]*int, 10)
|
||||
b := make([]*int, 10)
|
||||
go func() {
|
||||
a[5] = new(int)
|
||||
ch <- true
|
||||
}()
|
||||
copy(a[:5], b[:5])
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRacePointerSliceCopyWrite2(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]*int, 10)
|
||||
b := make([]*int, 10)
|
||||
go func() {
|
||||
b[5] = new(int)
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRacePointerSliceCopyRead(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]*int, 10)
|
||||
b := make([]*int, 10)
|
||||
go func() {
|
||||
_ = b[5]
|
||||
ch <- true
|
||||
}()
|
||||
copy(a, b)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceWriteSlice2(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]float64, 10)
|
||||
go func() {
|
||||
a[2] = 1.0
|
||||
ch <- true
|
||||
}()
|
||||
_ = a[0:5]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceWriteSlice(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]float64, 10)
|
||||
go func() {
|
||||
a[2] = 1.0
|
||||
ch <- true
|
||||
}()
|
||||
a = a[5:10]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceWriteSlice(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]float64, 10)
|
||||
go func() {
|
||||
a[2] = 1.0
|
||||
ch <- true
|
||||
}()
|
||||
_ = a[5:10]
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceSliceLenCap(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
a := make([]struct{}, 10)
|
||||
go func() {
|
||||
_ = len(a)
|
||||
ch <- true
|
||||
}()
|
||||
_ = cap(a)
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceStructSlicesRangeWrite(t *testing.T) {
|
||||
type Str struct {
|
||||
a []int
|
||||
b []int
|
||||
}
|
||||
ch := make(chan bool, 1)
|
||||
var s Str
|
||||
s.a = make([]int, 10)
|
||||
s.b = make([]int, 10)
|
||||
go func() {
|
||||
for range s.a {
|
||||
}
|
||||
ch <- true
|
||||
}()
|
||||
s.b[5] = 5
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestRaceSliceDifferent(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
s2 := s
|
||||
go func() {
|
||||
s[3] = 3
|
||||
c <- true
|
||||
}()
|
||||
// false negative because s2 is PAUTO w/o PHEAP
|
||||
// so we do not instrument it
|
||||
s2[3] = 3
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceRangeWrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s[3] = 3
|
||||
c <- true
|
||||
}()
|
||||
for _, v := range s {
|
||||
_ = v
|
||||
}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceSliceRangeWrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s[3] = 3
|
||||
c <- true
|
||||
}()
|
||||
for range s {
|
||||
}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceRangeAppend(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s = append(s, 3)
|
||||
c <- true
|
||||
}()
|
||||
for range s {
|
||||
}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceSliceRangeAppend(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
_ = append(s, 3)
|
||||
c <- true
|
||||
}()
|
||||
for range s {
|
||||
}
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarWrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s[3] = 3
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarRead(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
_ = s[3]
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarRange(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
for range s {
|
||||
}
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarAppend(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
_ = append(s, 10)
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarCopy(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s2 := make([]int, 10)
|
||||
copy(s, s2)
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceVarCopy2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s2 := make([]int, 10)
|
||||
copy(s2, s)
|
||||
c <- true
|
||||
}()
|
||||
s = make([]int, 20)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceAppend(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10, 20)
|
||||
go func() {
|
||||
_ = append(s, 1)
|
||||
c <- true
|
||||
}()
|
||||
_ = append(s, 2)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceAppendWrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
_ = append(s, 1)
|
||||
c <- true
|
||||
}()
|
||||
s[0] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceAppendSlice(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
go func() {
|
||||
s2 := make([]int, 10)
|
||||
_ = append(s, s2...)
|
||||
c <- true
|
||||
}()
|
||||
s[0] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceAppendSlice2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
s2foobar := make([]int, 10)
|
||||
go func() {
|
||||
_ = append(s, s2foobar...)
|
||||
c <- true
|
||||
}()
|
||||
s2foobar[5] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceAppendString(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]byte, 10)
|
||||
go func() {
|
||||
_ = append(s, "qwerty"...)
|
||||
c <- true
|
||||
}()
|
||||
s[0] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRacePointerSliceAppend(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]*int, 10, 20)
|
||||
go func() {
|
||||
_ = append(s, new(int))
|
||||
c <- true
|
||||
}()
|
||||
_ = append(s, new(int))
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRacePointerSliceAppendWrite(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]*int, 10)
|
||||
go func() {
|
||||
_ = append(s, new(int))
|
||||
c <- true
|
||||
}()
|
||||
s[0] = new(int)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRacePointerSliceAppendSlice(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]*int, 10)
|
||||
go func() {
|
||||
s2 := make([]*int, 10)
|
||||
_ = append(s, s2...)
|
||||
c <- true
|
||||
}()
|
||||
s[0] = new(int)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRacePointerSliceAppendSlice2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]*int, 10)
|
||||
s2foobar := make([]*int, 10)
|
||||
go func() {
|
||||
_ = append(s, s2foobar...)
|
||||
c <- true
|
||||
}()
|
||||
println("WRITE:", &s2foobar[5])
|
||||
s2foobar[5] = nil
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceSliceIndexAccess(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
v := 0
|
||||
go func() {
|
||||
_ = v
|
||||
c <- true
|
||||
}()
|
||||
s[v] = 1
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceSliceIndexAccess2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
v := 0
|
||||
go func() {
|
||||
_ = v
|
||||
c <- true
|
||||
}()
|
||||
_ = s[v]
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceIndexAccess(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
v := 0
|
||||
go func() {
|
||||
v = 1
|
||||
c <- true
|
||||
}()
|
||||
s[v] = 1
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceIndexAccess2(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
s := make([]int, 10)
|
||||
v := 0
|
||||
go func() {
|
||||
v = 1
|
||||
c <- true
|
||||
}()
|
||||
_ = s[v]
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceByteToString(t *testing.T) {
|
||||
c := make(chan string)
|
||||
s := make([]byte, 10)
|
||||
go func() {
|
||||
c <- string(s)
|
||||
}()
|
||||
s[0] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSliceRuneToString(t *testing.T) {
|
||||
c := make(chan string)
|
||||
s := make([]rune, 10)
|
||||
go func() {
|
||||
c <- string(s)
|
||||
}()
|
||||
s[9] = 42
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceConcatString(t *testing.T) {
|
||||
s := "hello"
|
||||
c := make(chan string, 1)
|
||||
go func() {
|
||||
c <- s + " world"
|
||||
}()
|
||||
s = "world"
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceCompareString(t *testing.T) {
|
||||
s1 := "hello"
|
||||
s2 := "world"
|
||||
c := make(chan bool, 1)
|
||||
go func() {
|
||||
c <- s1 == s2
|
||||
}()
|
||||
s1 = s2
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceSlice3(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
x := make([]int, 10)
|
||||
i := 2
|
||||
go func() {
|
||||
i = 3
|
||||
done <- true
|
||||
}()
|
||||
_ = x[:1:i]
|
||||
<-done
|
||||
}
|
||||
|
||||
var saved string
|
||||
|
||||
func TestRaceSlice4(t *testing.T) {
|
||||
// See issue 36794.
|
||||
data := []byte("hello there")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
_ = string(data)
|
||||
wg.Done()
|
||||
}()
|
||||
copy(data, data[2:])
|
||||
wg.Wait()
|
||||
}
|
||||
202
src/runtime/race/testdata/sync_test.go
vendored
Normal file
202
src/runtime/race/testdata/sync_test.go
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceCond(t *testing.T) {
|
||||
x := 0
|
||||
_ = x
|
||||
condition := 0
|
||||
var mu sync.Mutex
|
||||
cond := sync.NewCond(&mu)
|
||||
go func() {
|
||||
x = 1
|
||||
mu.Lock()
|
||||
condition = 1
|
||||
cond.Signal()
|
||||
mu.Unlock()
|
||||
}()
|
||||
mu.Lock()
|
||||
for condition != 1 {
|
||||
cond.Wait()
|
||||
}
|
||||
mu.Unlock()
|
||||
x = 2
|
||||
}
|
||||
|
||||
func TestRaceCond(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
var mu sync.Mutex
|
||||
cond := sync.NewCond(&mu)
|
||||
x := 0
|
||||
_ = x
|
||||
condition := 0
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond) // Enter cond.Wait loop
|
||||
x = 1
|
||||
mu.Lock()
|
||||
condition = 1
|
||||
cond.Signal()
|
||||
mu.Unlock()
|
||||
time.Sleep(10 * time.Millisecond) // Exit cond.Wait loop
|
||||
mu.Lock()
|
||||
x = 3
|
||||
mu.Unlock()
|
||||
done <- true
|
||||
}()
|
||||
mu.Lock()
|
||||
for condition != 1 {
|
||||
cond.Wait()
|
||||
}
|
||||
mu.Unlock()
|
||||
x = 2
|
||||
<-done
|
||||
}
|
||||
|
||||
// We do not currently automatically
|
||||
// parse this test. It is intended that the creation
|
||||
// stack is observed manually not to contain
|
||||
// off-by-one errors
|
||||
func TestRaceAnnounceThreads(t *testing.T) {
|
||||
const N = 7
|
||||
allDone := make(chan bool, N)
|
||||
|
||||
var x int
|
||||
_ = x
|
||||
|
||||
var f, g, h func()
|
||||
f = func() {
|
||||
x = 1
|
||||
go g()
|
||||
go func() {
|
||||
x = 1
|
||||
allDone <- true
|
||||
}()
|
||||
x = 2
|
||||
allDone <- true
|
||||
}
|
||||
|
||||
g = func() {
|
||||
for i := 0; i < 2; i++ {
|
||||
go func() {
|
||||
x = 1
|
||||
allDone <- true
|
||||
}()
|
||||
allDone <- true
|
||||
}
|
||||
}
|
||||
|
||||
h = func() {
|
||||
x = 1
|
||||
x = 2
|
||||
go f()
|
||||
allDone <- true
|
||||
}
|
||||
|
||||
go h()
|
||||
|
||||
for i := 0; i < N; i++ {
|
||||
<-allDone
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRaceAfterFunc1(t *testing.T) {
|
||||
i := 2
|
||||
c := make(chan bool)
|
||||
var f func()
|
||||
f = func() {
|
||||
i--
|
||||
if i >= 0 {
|
||||
time.AfterFunc(0, f)
|
||||
} else {
|
||||
c <- true
|
||||
}
|
||||
}
|
||||
|
||||
time.AfterFunc(0, f)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceAfterFunc2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
timer := time.AfterFunc(10, func() {
|
||||
x = 1
|
||||
})
|
||||
defer timer.Stop()
|
||||
}
|
||||
|
||||
func TestNoRaceAfterFunc3(t *testing.T) {
|
||||
c := make(chan bool, 1)
|
||||
x := 0
|
||||
_ = x
|
||||
time.AfterFunc(1e7, func() {
|
||||
x = 1
|
||||
c <- true
|
||||
})
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceAfterFunc3(t *testing.T) {
|
||||
c := make(chan bool, 2)
|
||||
x := 0
|
||||
_ = x
|
||||
time.AfterFunc(1e7, func() {
|
||||
x = 1
|
||||
c <- true
|
||||
})
|
||||
time.AfterFunc(2e7, func() {
|
||||
x = 2
|
||||
c <- true
|
||||
})
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
// This test's output is intended to be
|
||||
// observed manually. One should check
|
||||
// that goroutine creation stack is
|
||||
// comprehensible.
|
||||
func TestRaceGoroutineCreationStack(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var ch = make(chan bool, 1)
|
||||
|
||||
f1 := func() {
|
||||
x = 1
|
||||
ch <- true
|
||||
}
|
||||
f2 := func() { go f1() }
|
||||
f3 := func() { go f2() }
|
||||
f4 := func() { go f3() }
|
||||
|
||||
go f4()
|
||||
x = 2
|
||||
<-ch
|
||||
}
|
||||
|
||||
// A nil pointer in a mutex method call should not
|
||||
// corrupt the race detector state.
|
||||
// Used to hang indefinitely.
|
||||
func TestNoRaceNilMutexCrash(t *testing.T) {
|
||||
var mutex sync.Mutex
|
||||
panics := 0
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
mutex.Lock()
|
||||
panics++
|
||||
mutex.Unlock()
|
||||
} else {
|
||||
panic("no panic")
|
||||
}
|
||||
}()
|
||||
var othermutex *sync.RWMutex
|
||||
othermutex.RLock()
|
||||
}
|
||||
116
src/runtime/race/testdata/time_test.go
vendored
Normal file
116
src/runtime/race/testdata/time_test.go
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceAfterFunc(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
v = 2
|
||||
time.AfterFunc(1, f)
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
|
||||
func TestNoRaceAfterFuncReset(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
t := time.AfterFunc(time.Hour, f)
|
||||
t.Stop()
|
||||
v = 2
|
||||
t.Reset(1)
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
|
||||
func TestNoRaceTimer(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
v = 2
|
||||
t := time.NewTimer(1)
|
||||
go func() {
|
||||
<-t.C
|
||||
f()
|
||||
}()
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
|
||||
func TestNoRaceTimerReset(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
t := time.NewTimer(time.Hour)
|
||||
go func() {
|
||||
<-t.C
|
||||
f()
|
||||
}()
|
||||
t.Stop()
|
||||
v = 2
|
||||
t.Reset(1)
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
|
||||
func TestNoRaceTicker(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
v = 2
|
||||
t := time.NewTicker(1)
|
||||
go func() {
|
||||
<-t.C
|
||||
f()
|
||||
}()
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
|
||||
func TestNoRaceTickerReset(_ *testing.T) {
|
||||
v := 0
|
||||
_ = v
|
||||
c := make(chan int)
|
||||
f := func() {
|
||||
v = 1
|
||||
c <- 0
|
||||
}
|
||||
t := time.NewTicker(time.Hour)
|
||||
go func() {
|
||||
<-t.C
|
||||
f()
|
||||
}()
|
||||
t.Stop()
|
||||
v = 2
|
||||
t.Reset(1)
|
||||
<-c
|
||||
v = 3
|
||||
}
|
||||
360
src/runtime/race/testdata/waitgroup_test.go
vendored
Normal file
360
src/runtime/race/testdata/waitgroup_test.go
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNoRaceWaitGroup(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
n := 1
|
||||
for i := 0; i < n; i++ {
|
||||
wg.Add(1)
|
||||
j := i
|
||||
go func() {
|
||||
x = j
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestRaceWaitGroup(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
n := 2
|
||||
for i := 0; i < n; i++ {
|
||||
wg.Add(1)
|
||||
j := i
|
||||
go func() {
|
||||
x = j
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroup2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
x = 1
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
x = 2
|
||||
}
|
||||
|
||||
// incrementing counter in Add and locking wg's mutex
|
||||
func TestRaceWaitGroupAsMutex(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
c := make(chan bool, 2)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
wg.Add(+1)
|
||||
x = 1
|
||||
wg.Add(-1)
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
wg.Wait()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
wg.Add(+1)
|
||||
x = 2
|
||||
wg.Add(-1)
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
// Incorrect usage: Add is too late.
|
||||
func TestRaceWaitGroupWrongWait(t *testing.T) {
|
||||
c := make(chan bool, 2)
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
runtime.Gosched()
|
||||
x = 1
|
||||
wg.Done()
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
runtime.Gosched()
|
||||
x = 2
|
||||
wg.Done()
|
||||
c <- true
|
||||
}()
|
||||
wg.Wait()
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestRaceWaitGroupWrongAdd(t *testing.T) {
|
||||
c := make(chan bool, 2)
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
wg.Done()
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
wg.Done()
|
||||
c <- true
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
wg.Wait()
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupMultipleWait(t *testing.T) {
|
||||
c := make(chan bool, 2)
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
wg.Wait()
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
wg.Wait()
|
||||
c <- true
|
||||
}()
|
||||
wg.Wait()
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupMultipleWait2(t *testing.T) {
|
||||
c := make(chan bool, 2)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
c <- true
|
||||
}()
|
||||
go func() {
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
c <- true
|
||||
}()
|
||||
wg.Wait()
|
||||
<-c
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupMultipleWait3(t *testing.T) {
|
||||
const P = 3
|
||||
var data [P]int
|
||||
done := make(chan bool, P)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(P)
|
||||
for p := 0; p < P; p++ {
|
||||
go func(p int) {
|
||||
data[p] = 42
|
||||
wg.Done()
|
||||
}(p)
|
||||
}
|
||||
for p := 0; p < P; p++ {
|
||||
go func() {
|
||||
wg.Wait()
|
||||
for p1 := 0; p1 < P; p1++ {
|
||||
_ = data[p1]
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
for p := 0; p < P; p++ {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
// Correct usage but still a race
|
||||
func TestRaceWaitGroup2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
x = 1
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
x = 2
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupPanicRecover(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != "sync: negative WaitGroup counter" {
|
||||
t.Fatalf("Unexpected panic: %#v", err)
|
||||
}
|
||||
x = 2
|
||||
}()
|
||||
x = 1
|
||||
wg.Add(-1)
|
||||
}
|
||||
|
||||
// TODO: this is actually a panic-synchronization test, not a
|
||||
// WaitGroup test. Move it to another *_test file
|
||||
// Is it possible to get a race by synchronization via panic?
|
||||
func TestNoRaceWaitGroupPanicRecover2(t *testing.T) {
|
||||
var x int
|
||||
_ = x
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan bool, 1)
|
||||
var f func() = func() {
|
||||
x = 2
|
||||
ch <- true
|
||||
}
|
||||
go func() {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != "sync: negative WaitGroup counter" {
|
||||
}
|
||||
go f()
|
||||
}()
|
||||
x = 1
|
||||
wg.Add(-1)
|
||||
}()
|
||||
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupTransitive(t *testing.T) {
|
||||
x, y := 0, 0
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
x = 42
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(1e7)
|
||||
y = 42
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
_ = x
|
||||
_ = y
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupReuse(t *testing.T) {
|
||||
const P = 3
|
||||
var data [P]int
|
||||
var wg sync.WaitGroup
|
||||
for try := 0; try < 3; try++ {
|
||||
wg.Add(P)
|
||||
for p := 0; p < P; p++ {
|
||||
go func(p int) {
|
||||
data[p]++
|
||||
wg.Done()
|
||||
}(p)
|
||||
}
|
||||
wg.Wait()
|
||||
for p := 0; p < P; p++ {
|
||||
data[p]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupReuse2(t *testing.T) {
|
||||
const P = 3
|
||||
var data [P]int
|
||||
var wg sync.WaitGroup
|
||||
for try := 0; try < 3; try++ {
|
||||
wg.Add(P)
|
||||
for p := 0; p < P; p++ {
|
||||
go func(p int) {
|
||||
data[p]++
|
||||
wg.Done()
|
||||
}(p)
|
||||
}
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
for p := 0; p < P; p++ {
|
||||
data[p]++
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
wg.Wait()
|
||||
<-done
|
||||
for p := 0; p < P; p++ {
|
||||
data[p]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRaceWaitGroupReuse(t *testing.T) {
|
||||
const P = 3
|
||||
const T = 3
|
||||
done := make(chan bool, T)
|
||||
var wg sync.WaitGroup
|
||||
for try := 0; try < T; try++ {
|
||||
var data [P]int
|
||||
wg.Add(P)
|
||||
for p := 0; p < P; p++ {
|
||||
go func(p int) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
data[p]++
|
||||
wg.Done()
|
||||
}(p)
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
for p := 0; p < P; p++ {
|
||||
data[p]++
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
wg.Wait()
|
||||
}
|
||||
for try := 0; try < T; try++ {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRaceWaitGroupConcurrentAdd(t *testing.T) {
|
||||
const P = 4
|
||||
waiting := make(chan bool, P)
|
||||
var wg sync.WaitGroup
|
||||
for p := 0; p < P; p++ {
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
waiting <- true
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
for p := 0; p < P; p++ {
|
||||
<-waiting
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
33
src/runtime/race/timer_test.go
Normal file
33
src/runtime/race/timer_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimers(t *testing.T) {
|
||||
const goroutines = 8
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(goroutines)
|
||||
var mu sync.Mutex
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
ticker := time.NewTicker(1)
|
||||
defer ticker.Stop()
|
||||
for c := 0; c < 1000; c++ {
|
||||
<-ticker.C
|
||||
mu.Lock()
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user