Data race if used as a drop in replacement for `sync.Once`.
If you use Do to write something and then after that read that something (the exact use case in your example). Then there is data race if anyone else does a Reset and then does the Do again (e.g. while handling an http request a reset request and a second request come in before you finish).
IMO, atomic.Value is a better tool for the example you gave.
To demonstrate, add race_test.go as below and then do go test -v -race -cpu=8:
package resync
import (
"runtime"
"sync"
"testing"
)
var global int
var o Once
func something() {
o.Do(func() {
global++
})
x := global
for i := 0; i < 1e5; i++ {
x += global
}
}
func TestRace(t *testing.T) {
n := runtime.GOMAXPROCS(-1)
if n < 2 {
t.Skip("Run test with -race and -cpu")
}
n *= 2
var wg sync.WaitGroup
wg.Add(n)
for j := 0; j < n; j++ {
go func() {
something()
o.Reset()
wg.Done()
}()
}
wg.Wait()
}
What about a PR?
There is no PR for this. This protection cannot be squeezed into resync.Once. Using resync.Once, you'd assume you can safely use resources initialized in the function passed to Once.Do(). But that is not true once you allow resetting it, and by that allowing another concurrent run of the initialization.