Files
prysm/async/multilock_test.go
Preston Van Loon 2fd6bd8150 Add golang.org/x/tools modernize static analyzer and fix violations (#15946)
* Ran gopls modernize to fix everything

go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

* Override rules_go provided dependency for golang.org/x/tools to v0.38.0.

To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches.

* Fix buildtag violations and ignore buildtag violations in external

* Introduce modernize analyzer package.

* Add modernize "any" analyzer.

* Fix violations of any analyzer

* Add modernize "appendclipped" analyzer.

* Fix violations of appendclipped

* Add modernize "bloop" analyzer.

* Add modernize "fmtappendf" analyzer.

* Add modernize "forvar" analyzer.

* Add modernize "mapsloop" analyzer.

* Add modernize "minmax" analyzer.

* Fix violations of minmax analyzer

* Add modernize "omitzero" analyzer.

* Add modernize "rangeint" analyzer.

* Fix violations of rangeint.

* Add modernize "reflecttypefor" analyzer.

* Fix violations of reflecttypefor analyzer.

* Add modernize "slicescontains" analyzer.

* Add modernize "slicessort" analyzer.

* Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686.

* Add modernize "stringscutprefix" analyzer.

* Add modernize "stringsbuilder" analyzer.

* Fix violations of stringsbuilder analyzer.

* Add modernize "stringsseq" analyzer.

* Add modernize "testingcontext" analyzer.

* Add modernize "waitgroup" analyzer.

* Changelog fragment

* gofmt

* gazelle

* Add modernize "newexpr" analyzer.

* Disable newexpr until go1.26

* Add more details in WORKSPACE on how to update the override

* @nalepae feedback on min()

* gofmt

* Fix violations of forvar
2025-11-14 01:27:22 +00:00

342 lines
6.2 KiB
Go

/*
Copyright 2017 Albert Tedja
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package async
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestUnique(t *testing.T) {
var arr []string
a := assert.New(t)
arr = []string{"a", "b", "c"}
a.Equal(arr, unique(arr))
arr = []string{"a", "a", "a"}
a.Equal([]string{"a"}, unique(arr))
arr = []string{"a", "a", "b"}
a.Equal([]string{"a", "b"}, unique(arr))
arr = []string{"a", "b", "a"}
a.Equal([]string{"a", "b"}, unique(arr))
arr = []string{"a", "b", "c", "b", "d"}
a.Equal([]string{"a", "b", "c", "d"}, unique(arr))
}
func TestGetChan(t *testing.T) {
ch1 := getChan("a")
ch2 := getChan("aa")
ch3 := getChan("a")
a := assert.New(t)
a.NotEqual(ch1, ch2)
a.Equal(ch1, ch3)
}
func TestLockUnlock(_ *testing.T) {
var wg sync.WaitGroup
wg.Add(5)
go func() {
lock := NewMultilock("dog", "cat", "owl")
lock.Lock()
defer lock.Unlock()
<-time.After(100 * time.Millisecond)
wg.Done()
}()
go func() {
lock := NewMultilock("cat", "dog", "bird")
lock.Lock()
defer lock.Unlock()
<-time.After(100 * time.Millisecond)
wg.Done()
}()
go func() {
lock := NewMultilock("cat", "bird", "owl")
lock.Lock()
defer lock.Unlock()
<-time.After(100 * time.Millisecond)
wg.Done()
}()
go func() {
lock := NewMultilock("bird", "owl", "snake")
lock.Lock()
defer lock.Unlock()
<-time.After(100 * time.Millisecond)
wg.Done()
}()
go func() {
lock := NewMultilock("owl", "snake")
lock.Lock()
defer lock.Unlock()
<-time.After(1 * time.Second)
wg.Done()
}()
wg.Wait()
}
func TestLockUnlock_CleansUnused(t *testing.T) {
var wg sync.WaitGroup
wg.Go(func() {
lock := NewMultilock("dog", "cat", "owl")
lock.Lock()
assert.Equal(t, 3, len(locks.list))
lock.Unlock()
})
wg.Wait()
// We expect that unlocking completely cleared the locks list
// given all 3 lock keys were unused at time of unlock.
assert.Equal(t, 0, len(locks.list))
}
func TestLockUnlock_DoesNotCleanIfHeldElsewhere(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)
go func() {
lock := NewMultilock("cat")
lock.Lock()
// We take 200 milliseconds to release the lock on "cat"
<-time.After(200 * time.Millisecond)
lock.Unlock()
// Assert that at the end of this goroutine, all locks are cleared.
assert.Equal(t, 0, len(locks.list))
wg.Done()
}()
go func() {
lock := NewMultilock("dog", "cat", "owl")
lock.Lock()
// We release the locks after 100 milliseconds, and check that "cat" is not
// cleared as a lock for it is still held by the previous goroutine.
<-time.After(100 * time.Millisecond)
lock.Unlock()
assert.Equal(t, 1, len(locks.list))
_, ok := locks.list["cat"]
assert.Equal(t, true, ok)
wg.Done()
}()
wg.Wait()
// We expect that at the end of this test, all locks are cleared.
assert.Equal(t, 0, len(locks.list))
}
func TestYield(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)
var resources = map[string]int{}
go func() {
lock := NewMultilock("A", "C")
lock.Lock()
defer lock.Unlock()
for resources["ac"] == 0 {
lock.Yield()
}
resources["dc"] = 10
wg.Done()
}()
go func() {
lock := NewMultilock("D", "C")
lock.Lock()
defer lock.Unlock()
resources["ac"] = 5
for resources["dc"] == 0 {
lock.Yield()
}
wg.Done()
}()
wg.Wait()
assert.Equal(t, 5, resources["ac"])
assert.Equal(t, 10, resources["dc"])
}
func TestClean(t *testing.T) {
var wg sync.WaitGroup
wg.Add(3)
// some goroutine that holds multiple locks
go1done := make(chan bool, 1)
go func() {
Loop:
for {
select {
case <-go1done:
break Loop
default:
lock := NewMultilock("A", "B", "C", "E", "Z")
lock.Lock()
<-time.After(30 * time.Millisecond)
lock.Unlock()
}
}
wg.Done()
}()
// another goroutine
go2done := make(chan bool, 1)
go func() {
Loop:
for {
select {
case <-go2done:
break Loop
default:
lock := NewMultilock("B", "C", "K", "L", "Z")
lock.Lock()
<-time.After(200 * time.Millisecond)
lock.Unlock()
}
}
wg.Done()
}()
// this one cleans up the locks every 100 ms
done := make(chan bool, 1)
go func() {
c := time.Tick(100 * time.Millisecond)
Loop:
for {
select {
case <-done:
break Loop
case <-c:
Clean()
}
}
wg.Done()
}()
<-time.After(2 * time.Second)
go1done <- true
go2done <- true
<-time.After(1 * time.Second)
done <- true
wg.Wait()
assert.Equal(t, []string{}, Clean())
}
func TestBankAccountProblem(t *testing.T) {
var wg sync.WaitGroup
wg.Add(3)
joe := 50.0
susan := 100.0
// withdraw $80 from joe, only if balance is sufficient
go func() {
lock := NewMultilock("joe")
lock.Lock()
defer lock.Unlock()
for joe < 80.0 {
lock.Yield()
}
joe -= 80.0
wg.Done()
}()
// transfer $200 from susan to joe, only if balance is sufficient
go func() {
lock := NewMultilock("joe", "susan")
lock.Lock()
defer lock.Unlock()
for susan < 200.0 {
lock.Yield()
}
susan -= 200.0
joe += 200.0
wg.Done()
}()
// susan deposit $300 to cover balance
go func() {
lock := NewMultilock("susan")
lock.Lock()
defer lock.Unlock()
susan += 300.0
wg.Done()
}()
wg.Wait()
assert.Equal(t, 170.0, joe)
assert.Equal(t, 200.0, susan)
}
func TestSyncCondCompatibility(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)
cond := sync.NewCond(NewMultilock("A", "C"))
var testValues = [3]string{"foo", "bar", "fizz!"}
sharedRsc := testValues[0]
go func() {
cond.L.Lock()
for sharedRsc == testValues[0] {
cond.Wait()
}
sharedRsc = testValues[2]
cond.Broadcast()
cond.L.Unlock()
wg.Done()
}()
go func() {
cond.L.Lock()
sharedRsc = testValues[1]
cond.Broadcast()
for sharedRsc == testValues[1] {
cond.Wait()
}
cond.L.Unlock()
wg.Done()
}()
wg.Wait()
assert.Equal(t, testValues[2], sharedRsc)
}