You probably want to make the element type of the slice an atomic.Value, instead of an interface{}. You shouldn't need a mutex then.
On Fri, Mar 11, 2022 at 7:31 AM Cameron Elliott <gara...@gmail.com> wrote: > > > Ian, thank you very much for the suggestion to use atomics. > Unfortunately, for a standard SPSC ring buffer, I don't think it does the > trick. > > > I am attaching a simple ring buffer program at the end. > > If you run 'go run -race main.go' on the example, > a race will occur. > The race that occurs is a write, then read to the > same element of a slice on two different goroutines. > > Of course a race-detected is expected. > > This can be fixed by mutexing Put() and Get(), > because through some magic, mutexs affect the tables/tags > the race detector maintains in order to catch races. > > Using sync.atomic on the ring buffer indexes doesn't > affect the race-detector state for read and writes. > > I spent more time investigating, it seems there are > two ways to make a traditional ring buffer compatible > with the race detector: > > 1. Use build tags to conditionally Lock/Unlock mutexes > where you would not actually need them, in order to reset > the race detector on the object crossing goroutines. > > 2. Use the pragma //go:linkname to get access to the 'runtime.race' > functions, in order to call Enable()/Disable/ReleaseMerge/Aquire > as sync.Pool does in order to make the race detector happy. > > If there are other methods, please let me know! > Thanks for any feedback! > > Cameron/Seattle > > > > > > > > package main > > import ( > "sync/atomic" > "time" > ) > > type RB struct { > //mu sync.Mutex > buf []interface{} > start, end int64 > } > > > const N = 10 > > func NewRB() *RB { > return &RB{buf: make([]interface{}, N)} > } > > func (r *RB) Put(x interface{}) { > // mu.Lock() > // defer mu.Unlock() > > r.buf[r.end] = x > atomic.AddInt64(&r.end,1) > //r.end++ > r.end %= N > > } > > func (r *RB) Get() interface{} { > // mu.Lock() > // defer mu.Unlock() > > v := r.buf[r.start] > atomic.AddInt64(&r.start,1) > //r.start++ > r.start %= N > > return v > } > > func main() { > > r := NewRB() > > go func() { > r.Put(12345) > }() > > time.Sleep(time.Millisecond) > > a := r.Get().(int) > println(a) > } > > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/d7cc3410-c0bb-40d6-bf75-5e655ba3136en%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/d7cc3410-c0bb-40d6-bf75-5e655ba3136en%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfGDrre6Rc4E-npy33-W8CZy7_ZT-w%3DCc4LskR7N015j6Q%40mail.gmail.com.