Commit fd427154 authored by Chen Kai's avatar Chen Kai Committed by GitHub

cannon: Add more concurrency primitives tests (#12771)

* feat:Add more sync tests
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

* feat:add more sync tests
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

* fix:fix failed tests
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

* fix:add copyright credit and remove unused code
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

* fix:fix copyright format
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

* fix code review suggestions
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>

---------
Signed-off-by: default avatarChen Kai <281165273grape@gmail.com>
parent 01559d2e
......@@ -36,27 +36,90 @@ func TestInstrumentedState_Claim(t *testing.T) {
func TestInstrumentedState_MultithreadedProgram(t *testing.T) {
t.Parallel()
state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath("multithreaded"), CreateInitialState, false)
oracle := testutil.StaticOracle(t, []byte{})
var stdOutBuf, stdErrBuf bytes.Buffer
us := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger(), nil)
for i := 0; i < 2_000_000; i++ {
if us.GetState().GetExited() {
break
}
_, err := us.Step(false)
require.NoError(t, err)
cases := []struct {
name string
expectedOutput []string
programName string
}{
{
name: "wg and chan test",
expectedOutput: []string{
"waitgroup result: 42",
"channels result: 1234",
},
programName: "mt-wg",
},
{
name: "mutex test",
expectedOutput: []string{
"Mutex test passed",
},
programName: "mt-mutex",
},
{
name: "cond test",
expectedOutput: []string{
"Cond test passed",
},
programName: "mt-cond",
},
{
name: "rwmutex test",
expectedOutput: []string{
"RWMutex test passed",
},
programName: "mt-rwmutex",
},
{
name: "once test",
expectedOutput: []string{
"Once test passed",
},
programName: "mt-once",
},
{
name: "map test",
expectedOutput: []string{
"Map test passed",
},
programName: "mt-map",
},
{
name: "pool test",
expectedOutput: []string{
"Pool test passed",
},
programName: "mt-pool",
},
}
t.Logf("Completed in %d steps", state.Step)
require.True(t, state.Exited, "must complete program")
require.Equal(t, uint8(0), state.ExitCode, "exit with 0")
require.Contains(t, "waitgroup result: 42", stdErrBuf.String())
require.Contains(t, "channels result: 1234", stdErrBuf.String())
require.Equal(t, "", stdErrBuf.String(), "should not print any errors")
}
for _, test := range cases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath(test.programName), CreateInitialState, false)
oracle := testutil.StaticOracle(t, []byte{})
var stdOutBuf, stdErrBuf bytes.Buffer
us := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger(), nil)
for i := 0; i < 5_000_000; i++ {
if us.GetState().GetExited() {
break
}
_, err := us.Step(false)
require.NoError(t, err)
}
t.Logf("Completed in %d steps", state.Step)
require.True(t, state.Exited, "must complete program")
require.Equal(t, uint8(0), state.ExitCode, "exit with 0")
for _, expected := range test.expectedOutput {
require.Contains(t, stdOutBuf.String(), expected)
}
require.Equal(t, "", stdErrBuf.String(), "should not print any errors")
})
}
}
func TestInstrumentedState_Alloc(t *testing.T) {
if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
......
// Portions of this code are derived from code written by The Go Authors.
// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/cond_test.go
//
// --- Original License Notice ---
//
// Copyright 2009 The Go Authors.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"fmt"
"os"
"reflect"
"runtime"
"sync"
)
func main() {
TestCondSignal()
TestCondSignalGenerations()
TestCondBroadcast()
TestRace()
TestCondSignalStealing()
TestCondCopy()
fmt.Println("Cond test passed")
}
func TestCondSignal() {
var m sync.Mutex
c := sync.NewCond(&m)
n := 2
running := make(chan bool, n)
awake := make(chan bool, n)
for i := 0; i < n; i++ {
go func() {
m.Lock()
running <- true
c.Wait()
awake <- true
m.Unlock()
}()
}
for i := 0; i < n; i++ {
<-running // Wait for everyone to run.
}
for n > 0 {
select {
case <-awake:
_, _ = fmt.Fprintln(os.Stderr, "goroutine not asleep")
os.Exit(1)
default:
}
m.Lock()
c.Signal()
m.Unlock()
<-awake // Will deadlock if no goroutine wakes up
select {
case <-awake:
_, _ = fmt.Fprintln(os.Stderr, "too many goroutines awake")
os.Exit(1)
default:
}
n--
}
c.Signal()
}
func TestCondSignalGenerations() {
var m sync.Mutex
c := sync.NewCond(&m)
n := 100
running := make(chan bool, n)
awake := make(chan int, n)
for i := 0; i < n; i++ {
go func(i int) {
m.Lock()
running <- true
c.Wait()
awake <- i
m.Unlock()
}(i)
if i > 0 {
a := <-awake
if a != i-1 {
_, _ = fmt.Fprintf(os.Stderr, "wrong goroutine woke up: want %d, got %d\n", i-1, a)
os.Exit(1)
}
}
<-running
m.Lock()
c.Signal()
m.Unlock()
}
}
func TestCondBroadcast() {
var m sync.Mutex
c := sync.NewCond(&m)
n := 5
running := make(chan int, n)
awake := make(chan int, n)
exit := false
for i := 0; i < n; i++ {
go func(g int) {
m.Lock()
for !exit {
running <- g
c.Wait()
awake <- g
}
m.Unlock()
}(i)
}
for i := 0; i < n; i++ {
for i := 0; i < n; i++ {
<-running // Will deadlock unless n are running.
}
if i == n-1 {
m.Lock()
exit = true
m.Unlock()
}
select {
case <-awake:
_, _ = fmt.Fprintln(os.Stderr, "goroutine not asleep")
os.Exit(1)
default:
}
m.Lock()
c.Broadcast()
m.Unlock()
seen := make([]bool, n)
for i := 0; i < n; i++ {
g := <-awake
if seen[g] {
_, _ = fmt.Fprintln(os.Stderr, "goroutine woke up twice")
os.Exit(1)
}
seen[g] = true
}
}
select {
case <-running:
_, _ = fmt.Fprintln(os.Stderr, "goroutine still running")
os.Exit(1)
default:
}
c.Broadcast()
}
func TestRace() {
x := 0
c := sync.NewCond(&sync.Mutex{})
done := make(chan bool)
go func() {
c.L.Lock()
x = 1
c.Wait()
if x != 2 {
_, _ = fmt.Fprintln(os.Stderr, "want 2")
os.Exit(1)
}
x = 3
c.Signal()
c.L.Unlock()
done <- true
}()
go func() {
c.L.Lock()
for {
if x == 1 {
x = 2
c.Signal()
break
}
c.L.Unlock()
runtime.Gosched()
c.L.Lock()
}
c.L.Unlock()
done <- true
}()
go func() {
c.L.Lock()
for {
if x == 2 {
c.Wait()
if x != 3 {
_, _ = fmt.Fprintln(os.Stderr, "want 3")
os.Exit(1)
}
break
}
if x == 3 {
break
}
c.L.Unlock()
runtime.Gosched()
c.L.Lock()
}
c.L.Unlock()
done <- true
}()
<-done
<-done
<-done
}
func TestCondSignalStealing() {
for iters := 0; iters < 5; iters++ {
var m sync.Mutex
cond := sync.NewCond(&m)
// Start a waiter.
ch := make(chan struct{})
go func() {
m.Lock()
ch <- struct{}{}
cond.Wait()
m.Unlock()
ch <- struct{}{}
}()
<-ch
m.Lock()
m.Unlock()
// We know that the waiter is in the cond.Wait() call because we
// synchronized with it, then acquired/released the mutex it was
// holding when we synchronized.
//
// Start two goroutines that will race: one will broadcast on
// the cond var, the other will wait on it.
//
// The new waiter may or may not get notified, but the first one
// has to be notified.
done := false
go func() {
cond.Broadcast()
}()
go func() {
m.Lock()
for !done {
cond.Wait()
}
m.Unlock()
}()
// Check that the first waiter does get signaled.
<-ch
// Release the second waiter in case it didn't get the
// broadcast.
m.Lock()
done = true
m.Unlock()
cond.Broadcast()
}
}
func TestCondCopy() {
defer func() {
err := recover()
if err == nil || err.(string) != "sync.Cond is copied" {
_, _ = fmt.Fprintf(os.Stderr, "got %v, expect sync.Cond is copied", err)
os.Exit(1)
}
}()
c := sync.Cond{L: &sync.Mutex{}}
c.Signal()
var c2 sync.Cond
reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet
c2.Signal()
}
module map
go 1.22
toolchain go1.22.0
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
m.Store("hello", "world")
m.Store("foo", "bar")
m.Store("baz", "qux")
m.Delete("foo")
m.Load("baz")
go func() {
m.CompareAndDelete("hello", "world")
m.LoadAndDelete("baz")
}()
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
m.Load("hello")
m.Load("baz")
m.Range(func(k, v interface{}) bool {
m.Load("hello")
m.Load("baz")
return true
})
m.CompareAndSwap("hello", "world", "Go")
m.LoadOrStore("hello", "world")
wg.Done()
}()
}
wg.Wait()
fmt.Println("Map test passed")
}
module mutex
go 1.22
toolchain go1.22.0
// Portions of this code are derived from code written by The Go Authors.
// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/mutex_test.go
//
// --- Original License Notice ---
//
// Copyright 2009 The Go Authors.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"fmt"
"os"
"sync"
)
func main() {
TestMutex()
}
func TestMutex() {
m := new(sync.Mutex)
m.Lock()
if m.TryLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex locked")
os.Exit(1)
}
m.Unlock()
if !m.TryLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryLock failed with mutex unlocked")
os.Exit(1)
}
m.Unlock()
c := make(chan bool)
for i := 0; i < 10; i++ {
go HammerMutex(m, 1000, c)
}
for i := 0; i < 10; i++ {
<-c
}
fmt.Println("Mutex test passed")
}
func HammerMutex(m *sync.Mutex, loops int, cdone chan bool) {
for i := 0; i < loops; i++ {
if i%3 == 0 {
if m.TryLock() {
m.Unlock()
}
continue
}
m.Lock()
m.Unlock()
}
cdone <- true
}
module once
go 1.22
toolchain go1.22.0
// Portions of this code are derived from code written by The Go Authors.
// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/once_test.go
//
// --- Original License Notice ---
//
// Copyright 2009 The Go Authors.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"fmt"
"os"
"sync"
)
func main() {
TestOnce()
TestOncePanic()
fmt.Println("Once test passed")
}
type one int
func (o *one) Increment() {
*o++
}
func run(once *sync.Once, o *one, c chan bool) {
once.Do(func() { o.Increment() })
if v := *o; v != 1 {
_, _ = fmt.Fprintf(os.Stderr, "once failed inside run: %d is not 1\n", v)
os.Exit(1)
}
c <- true
}
func TestOnce() {
o := new(one)
once := new(sync.Once)
c := make(chan bool)
const N = 10
for i := 0; i < N; i++ {
go run(once, o, c)
}
for i := 0; i < N; i++ {
<-c
}
if *o != 1 {
_, _ = fmt.Fprintf(os.Stderr, "once failed outside run: %d is not 1\n", *o)
os.Exit(1)
}
}
func TestOncePanic() {
var once sync.Once
func() {
defer func() {
if r := recover(); r == nil {
_, _ = fmt.Fprintf(os.Stderr, "Once.Do did not panic")
os.Exit(1)
}
}()
once.Do(func() {
panic("failed")
})
}()
once.Do(func() {
_, _ = fmt.Fprintf(os.Stderr, "Once.Do called twice")
os.Exit(1)
})
}
module pool
go 1.22
toolchain go1.22.0
package main
import (
"fmt"
"sync"
)
func main() {
var x sync.Pool
x.Put(1)
x.Put(2)
// try some concurrency!
var wg sync.WaitGroup
wg.Add(2)
go func() {
x.Put(3)
wg.Done()
}()
go func() {
x.Put(4)
wg.Done()
}()
wg.Wait()
wg.Add(4)
for i := 0; i < 4; i++ {
go func() {
x.Get()
wg.Done()
}()
}
wg.Wait()
fmt.Println("Pool test passed")
}
module rwmutex
go 1.22
toolchain go1.22.0
// Portions of this code are derived from code written by The Go Authors.
// See original source: https://github.com/golang/go/blob/400433af3660905ecaceaf19ddad3e6c24b141df/src/sync/rwmutex_test.go
//
// --- Original License Notice ---
//
// Copyright 2009 The Go Authors.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"fmt"
"os"
"runtime"
"sync"
"sync/atomic"
)
func main() {
TestParallelReaders()
TestRLocker()
TestRWMutex()
fmt.Println("RWMutex test passed")
}
func parallelReader(m *sync.RWMutex, clocked, cunlock, cdone chan bool) {
m.RLock()
clocked <- true
<-cunlock
m.RUnlock()
cdone <- true
}
func doTestParallelReaders(numReaders, gomaxprocs int) {
runtime.GOMAXPROCS(gomaxprocs)
var m sync.RWMutex
clocked := make(chan bool)
cunlock := make(chan bool)
cdone := make(chan bool)
for i := 0; i < numReaders; i++ {
go parallelReader(&m, clocked, cunlock, cdone)
}
// Wait for all parallel RLock()s to succeed.
for i := 0; i < numReaders; i++ {
<-clocked
}
for i := 0; i < numReaders; i++ {
cunlock <- true
}
// Wait for the goroutines to finish.
for i := 0; i < numReaders; i++ {
<-cdone
}
}
func TestParallelReaders() {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
doTestParallelReaders(1, 4)
doTestParallelReaders(3, 4)
doTestParallelReaders(4, 2)
}
func reader(rwm *sync.RWMutex, num_iterations int, activity *int32, cdone chan bool) {
for i := 0; i < num_iterations; i++ {
rwm.RLock()
n := atomic.AddInt32(activity, 1)
if n < 1 || n >= 10000 {
rwm.RUnlock()
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -1)
rwm.RUnlock()
}
cdone <- true
}
func writer(rwm *sync.RWMutex, num_iterations int, activity *int32, cdone chan bool) {
for i := 0; i < num_iterations; i++ {
rwm.Lock()
n := atomic.AddInt32(activity, 10000)
if n != 10000 {
rwm.Unlock()
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -10000)
rwm.Unlock()
}
cdone <- true
}
func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
runtime.GOMAXPROCS(gomaxprocs)
// Number of active readers + 10000 * number of active writers.
var activity int32
var rwm sync.RWMutex
cdone := make(chan bool)
go writer(&rwm, num_iterations, &activity, cdone)
var i int
for i = 0; i < numReaders/2; i++ {
go reader(&rwm, num_iterations, &activity, cdone)
}
go writer(&rwm, num_iterations, &activity, cdone)
for ; i < numReaders; i++ {
go reader(&rwm, num_iterations, &activity, cdone)
}
// Wait for the 2 writers and all readers to finish.
for i := 0; i < 2+numReaders; i++ {
<-cdone
}
}
func TestRWMutex() {
var m sync.RWMutex
m.Lock()
if m.TryLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex locked")
os.Exit(1)
}
if m.TryRLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryRLock succeeded with mutex locked")
os.Exit(1)
}
m.Unlock()
if !m.TryLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryLock failed with mutex unlocked")
os.Exit(1)
}
m.Unlock()
if !m.TryRLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryRLock failed with mutex unlocked")
os.Exit(1)
}
if !m.TryRLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryRLock failed with mutex unlocked")
os.Exit(1)
}
if m.TryLock() {
_, _ = fmt.Fprintln(os.Stderr, "TryLock succeeded with mutex rlocked")
os.Exit(1)
}
m.RUnlock()
m.RUnlock()
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
n := 5
HammerRWMutex(1, 1, n)
HammerRWMutex(1, 3, n)
HammerRWMutex(1, 10, n)
HammerRWMutex(4, 1, n)
HammerRWMutex(4, 3, n)
HammerRWMutex(4, 10, n)
HammerRWMutex(10, 1, n)
HammerRWMutex(10, 3, n)
HammerRWMutex(10, 10, n)
HammerRWMutex(10, 5, n)
}
func TestRLocker() {
var wl sync.RWMutex
var rl sync.Locker
wlocked := make(chan bool, 1)
rlocked := make(chan bool, 1)
rl = wl.RLocker()
n := 10
go func() {
for i := 0; i < n; i++ {
rl.Lock()
rl.Lock()
rlocked <- true
wl.Lock()
wlocked <- true
}
}()
for i := 0; i < n; i++ {
<-rlocked
rl.Unlock()
select {
case <-wlocked:
_, _ = fmt.Fprintln(os.Stderr, "RLocker() didn't read-lock it")
os.Exit(1)
default:
}
rl.Unlock()
<-wlocked
select {
case <-rlocked:
_, _ = fmt.Fprintln(os.Stderr, "RLocker() didn't respect the write lock")
os.Exit(1)
default:
}
wl.Unlock()
}
}
module wg
go 1.22
toolchain go1.22.0
......@@ -2,8 +2,6 @@ package main
import (
"fmt"
"os"
"runtime"
"sync"
"sync/atomic"
)
......@@ -39,8 +37,4 @@ func main() {
a <- 1234
out := <-c
fmt.Printf("channels result: %d\n", out)
// try a GC! (the runtime might not have run one yet)
runtime.GC()
_, _ = os.Stdout.Write([]byte("GC complete!\n"))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment