I found a lock-free ringbuffer implementation written in C, which seems to do what you want: https://github.com/QuantumLeaps/lock-free-ring-buffer/blob/main/src/ This is a relatively direct translation to Go, using generics from go 1.18 (to be released very soon): https://go.dev/play/p/nNEt66r71Yf?v=gotip I tried running it with -race and got no complaints.
On Fri, Mar 11, 2022 at 7:52 AM Axel Wagner <axel.wagner...@googlemail.com> wrote: > Uhm, I just actually looked at the code. > You still use `r.start %= N`, which is a non-atomic access to r.start. > AIUI, SPSC stands for "single producer, single consumer", i.e. you know > that Get and Put will only be called from a single goroutine, respectively? > In that case, you wouldn't even need atomics to manipulate r.start/r.end. > Of course, you can't have a guarantee that your ringbuffer does not > overflow, that way. > But ISTM to make the conceptual code work with the race detector, you > should use atomic.Values for the elements of the slice and then can use > non-atomic accesses to r.start/r.end. > > On Fri, Mar 11, 2022 at 7:45 AM Axel Wagner <axel.wagner...@googlemail.com> > wrote: > >> 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/CAEkBMfEePeUQHqUYUxH_FrPxMGbUdLbsXXGLv2sq%2BwHWx97agg%40mail.gmail.com.