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.

Reply via email to