Or the same demonstration without a very confusing overloaded identifier 
(sorry!):
https://go.dev/play/p/ChOkAyV8imA?v=gotip

On Friday, March 11, 2022 at 8:52:04 AM UTC-7 Gregg Townsend wrote:

> Well, the suggested code passes the race detector, but shouldn't it also 
> be *correct*?
> Two concurrent calls of Put can load identical head and tail values and 
> then store in the same slot.
> See
> https://go.dev/play/p/gagBCCj2n2g?v=gotip
> for a demonstration.
>
>
> On Friday, March 11, 2022 at 12:24:42 AM UTC-7 axel.wa...@googlemail.com 
> wrote:
>
>> 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.wa...@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.wa...@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 <gar...@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...@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/af2d77f4-df67-47c1-b949-0e0e6a9c185bn%40googlegroups.com.

Reply via email to