resync icon indicating copy to clipboard operation
resync copied to clipboard

Data race if used as a drop in replacement for `sync.Once`.

Open dchapes opened this issue 10 years ago • 2 comments

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()
}

dchapes avatar May 02 '15 00:05 dchapes

What about a PR?

matryer avatar Jun 30 '16 15:06 matryer

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.

icza avatar Jun 26 '19 11:06 icza