Last active
December 6, 2023 23:54
-
-
Save Grabber/556674fa180c98f4062bd576476d98b0 to your computer and use it in GitHub Desktop.
Golang / sync.WaitGroup / data race investigation with nested structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"sync" | |
"time" | |
) | |
type Session struct { | |
Id string | |
wg *sync.WaitGroup | |
} | |
type ClientMessage struct { | |
Sess *Session | |
} | |
func main() { | |
doneCh := make(chan any) | |
sess := Session{ | |
Id: "OoOoOoOo", | |
wg: new(sync.WaitGroup), | |
} | |
sess.wg.Add(1) | |
go func(sess *Session) { | |
for i := 0; i < 3; i++ { | |
fmt.Printf("go func(sess *Session): %v -> %d\n", sess, i) | |
time.Sleep(500 * time.Millisecond) | |
} | |
sess.wg.Done() | |
}(&sess) | |
cm := ClientMessage{ | |
Sess: &sess, | |
} | |
sess.wg.Add(1) | |
go func(cm *ClientMessage) { | |
for i := 0; i < 10; i++ { | |
fmt.Println("loop") | |
fmt.Printf("go func(cm *ClientMessage): %v -> %d\n", cm.Sess, i) | |
fmt.Printf("go func(cm *ClientMessage): %v -> %d\n", cm.Sess.Id, i) | |
time.Sleep(1 * time.Second) | |
} | |
cm.Sess.wg.Done() | |
}(&cm) | |
cmb := ClientMessage{ | |
Sess: &sess, | |
} | |
sess.wg.Add(1) | |
go func(cm *ClientMessage) { | |
for i := 0; i < 10; i++ { | |
fmt.Printf("go func(cm *ClientMessage): %d\n", i) | |
fmt.Printf("go func(cm *ClientMessage): %v -> %d\n", cm.Sess, i) | |
time.Sleep(1 * time.Second) | |
} | |
cm.Sess.wg.Done() | |
}(&cmb) | |
go func(cm *ClientMessage) { | |
fmt.Println("func(cm *ClientMessage),1") | |
cm.Sess.wg.Wait() | |
fmt.Println("func(cm *ClientMessage),2") | |
doneCh <- struct{}{} | |
fmt.Println("func(cm *ClientMessage),3") | |
}(&cm) | |
fmt.Println("Done,1") | |
<-doneCh | |
fmt.Println("Done,3") | |
} |
I found a very interesting data race when using a sync.WaitGroup implace to a nested struct with some gorountines concurrently calling .Done() while another goroutine is waiting on .Wait(). To avoid the race condition, wg
must be a pointer and be explicity initialized with new(sync.WaitGroup).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is the output when using non-pointer: wg sync.WaitGroup