Btw, for those interesting in some neat highly optimized queue patterns check 
out https://lmax-exchange.github.io/disruptor/

> On Mar 11, 2022, at 5:58 PM, Robert Engels <reng...@ix.netcom.com> wrote:
> 
> 
> “Usually” SPSC means it is optimized for that case but it doesn’t fail in 
> case of multiple writers. It it usually easier to prevent multiple readers as 
> the reader can be controlled during init and hidden. 
> 
>>> On Mar 11, 2022, at 4:41 PM, 'Axel Wagner' via golang-nuts 
>>> <golang-nuts@googlegroups.com> wrote:
>>> 
>> 
>> 
>> 
>>> On Fri, Mar 11, 2022 at 4:51 PM Gregg Townsend <gtowns...@gmail.com> 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.
>> 
>> The premise is that there is a single consumer and a single producer (hence 
>> "SPSC").
>> The assumption was that OP specifically wanted something fitting that 
>> use-case.
>> If you have multiple producers/consumers, a buffered channel is obviously a 
>> better alternative.
>>  
>>> 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.
>>> 
>>> -- 
>>> 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/900d07ba-ce11-4f1a-94d2-609b5b8260c7n%40googlegroups.com.
>> 
>> -- 
>> 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/CAEkBMfFmgxpJpj8zSpMWtnAK6qkkvzuR4%3DFUJriZF_Jku6PGDg%40mail.gmail.com.

-- 
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/26682F46-21BC-41CF-BC0D-58622DE3120B%40ix.netcom.com.

Reply via email to