Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: make tests deterministic with -scheduler=threads #4640

Merged
merged 1 commit into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions testdata/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"runtime"
"sync"
"sync/atomic"
"time"
)

Expand Down Expand Up @@ -70,11 +71,13 @@ func main() {
// Test multi-receiver.
ch = make(chan int)
wg.Add(3)
go fastreceiver(ch)
go fastreceiver(ch)
go fastreceiver(ch)
var result atomic.Uint32
go fastreceiveradd(ch, &result)
go fastreceiveradd(ch, &result)
go fastreceiveradd(ch, &result)
slowsender(ch)
wg.Wait()
println("sum of sums:", result.Load())

// Test iterator style channel.
ch = make(chan int)
Expand All @@ -88,7 +91,10 @@ func main() {
println("sum(100):", sum)

// Test simple selects.
go selectDeadlock() // cannot use waitGroup here - never terminates
wg.Add(1)
go selectDeadlock()
wg.Wait()
Comment on lines -91 to +96
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly this was easily fixable using a WaitGroup.


wg.Add(1)
go selectNoOp()
wg.Wait()
Expand Down Expand Up @@ -244,7 +250,7 @@ func receive(ch <-chan int) {
func sender(ch chan int) {
for i := 1; i <= 8; i++ {
if i == 4 {
time.Sleep(time.Microsecond)
time.Sleep(time.Millisecond)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be long enough to make the test deterministic with the Go toolchain.
But of course it's still racy, if it becomes a problem we can try to find another way using a WaitGroup or something.

println("slept")
}
ch <- i
Expand Down Expand Up @@ -290,6 +296,16 @@ func fastreceiver(ch chan int) {
wg.Done()
}

func fastreceiveradd(ch chan int, result *atomic.Uint32) {
sum := 0
for i := 0; i < 2; i++ {
n := <-ch
sum += n
}
result.Add(uint32(sum))
wg.Done()
}

func iterator(ch chan int, top int) {
for i := 0; i < top; i++ {
ch <- i
Expand All @@ -300,6 +316,7 @@ func iterator(ch chan int, top int) {

func selectDeadlock() {
println("deadlocking")
wg.Done()
select {}
println("unreachable")
}
Expand Down
4 changes: 1 addition & 3 deletions testdata/channel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ received num: 8
recv from closed channel: 0 false
complex128: (+7.000000e+000+1.050000e+001i)
sum of n: 149
sum: 25
sum: 29
sum: 33
sum of sums: 87
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These numbers were spread out differently when using threading. Sometimes the right numbers came out, but other times the numbers were balanced differently. Of course that's all correct: the important part is that every number is received by exactly one receiver.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mathematician in me really wants this to switch from adding integers to multiplying unique primes so that we can be sure (due to https://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic ) that everything was received exactly once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that's not a bad idea!
Not sure I'll make that PR, but you're free to do so of course :)

sum(100): 4950
deadlocking
select no-op
Expand Down
2 changes: 1 addition & 1 deletion testdata/goroutines.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ func acquire(m *sync.Mutex) {
m.Lock()
println("acquired mutex from goroutine")
time.Sleep(2 * time.Millisecond)
println("releasing mutex from goroutine")
m.Unlock()
println("released mutex from goroutine")
}

func sub() {
Expand Down
2 changes: 1 addition & 1 deletion testdata/goroutines.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ closure go call result: 1
pre-acquired mutex
releasing mutex
acquired mutex from goroutine
released mutex from goroutine
releasing mutex from goroutine
re-acquired mutex
done
called: Foo.Nowait
Expand Down
8 changes: 6 additions & 2 deletions testdata/recover.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package main

import (
"runtime"
"time"
"sync"
)

var wg sync.WaitGroup

func main() {
println("# simple recover")
recoverSimple()
Expand Down Expand Up @@ -113,14 +115,16 @@ func deferPanic() {
}

func runtimeGoexit() {
wg.Add(1)
go func() {
defer func() {
println("Goexit deferred function, recover is nil:", recover() == nil)
wg.Done()
}()

runtime.Goexit()
}()
time.Sleep(time.Millisecond)
wg.Wait()
}

func printitf(msg string, itf interface{}) {
Expand Down