On Tue, Nov 8, 2016 at 8:24 PM, Oliver Kowalke <
oliver....@gmail.com> wrote:
> 2016-11-08 17:49 GMT+01:00 Dmitry Vyukov <
dvy...@gmail.com>:
>>
>> On Mon, Nov 7, 2016 at 5:05 AM, Oliver Kowalke <
oliver....@gmail.com>
>> wrote:
>> > 2016-11-06 17:10 GMT+01:00 Dmitry Vyukov <
dvy...@gmail.com>:
>> >>
>> >> > cell = &buffer_[pos & (buffer_mask_ - 1)];
>> >> Why? What is the problem you are trying to fix with this change?
>> >
>> >
>> > pos is incremented by the producer and because the buffer is bound we
>> > need
>> > to wrap if the last element of the buffer was accessed
>> > for a buffer with 4 elements the Index will be:
>> > 0: 0
>> > 1: 1
>> > 2: 2
>> > 3: 3
>> > 4: 0
>>
>> This is a buffer with 5 elements, not 4.
>
>
> the first number is the index/position/enqueue-position - the second number
> corresponds to the array index
> test code:
>
> int mask = 4;
> for (int pos = 0; pos < 8; ++pos) {
> std::cout << pos << ": " << (pos & (mask - 1)) << std::endl;
> }
>
> produces:
> 0: 0
> 1: 1
> 2: 2
> 3: 3
> 4: 0 // wrapped first time
> 5: 1
> 6: 2
> 7: 3
>
>
> while in the other code:
>
> int mask = 4;
> for (int pos = 0; pos < 8; ++pos) {
> std::cout << pos << ": " << (pos & mask) << std::endl;
> }
>
> produces:
> 0: 0
> 1: 0
> 2: 0
> 3: 0
> 4: 4
> 5: 4
> 6: 4
> 7: 4
+lock-free again, please keep it in CC
Did you notice that my code initializes mask to size-1:
, buffer_mask_(buffer_size - 1)
?
So if buffer size is 4, mask is 3 and (pos & mask) working fine.
>> If producer wraps around: consumer pos = X, producer pos =
>> X+buffer_size, so sequence - pos = buffer_size -> producer is not
>> allowed to write. I don't see the problem.
>
>
> sorry I was referring to cell->sequence, not the indices. In your code you
> suggest to store struct cell
> in each buffer slot. struct cell has a member named sequence. sequence is
> of type std::atomic< int > and
> used to synchronize producers and consumers.
>
> producer: cell->sequence_.store(pos + 1, std::memory_order_release);
> consumer: cell->sequence_.store(pos + buffer_mask_ + 1,
> std::memory_order_release);
>
> suppose a buffer with 4 slots - sequence would contain following values
> after access from a producer:
> mask = 4
>
> 1. round:
> array index 0 1 2 3
> pos 0 1 2 3
> sequence_ (after producer's access) 1 2 3 4
> sequence_ (after consumer's access) 5 6 7 8
>
> 2. round:
> array index 0 1 2 3
> pos 4 5
> 6 7
> sequence_ (after producer's access) 5 6 7 8
> sequence_ (after consumer's access) 9 10 11 12
>
> if a producer wraps it is only allowed the use the cell at the first element
> in the buffer if sequence_ - pos == 0, e.g.
> a consumer has dequeued the value and set sequence_ = pos + buffer_mask +
> 1, e.g. the first consumer in 1. round has set
> sequence_ = 0 + 4 +1 = 5. But the producer in 2.round is never allowed to
> enqueue its element because the condition for enqueueing
> is false: (sequence_ - pos == 0 -> 5 - 4 != 0).
>
> with a slight modification in how the consumer computes sequence_ your code
> works:
> cell->sequence_.store(pos + buffer_mask_, std::memory_order_release);
>
> 1. round:
> array index 0 1 2 3
> pos 0 1 2 3
> sequence_ (after producer's access) 1 2 3 4
> sequence_ (after consumer's access) 4 5 6 7
>
> 2. round:
> array index 0 1 2 3
> pos 4 5
> 6 7
> sequence_ (after producer's access) 5 6 7 8
> sequence_ (after consumer's access) 8 9 10 11
>
> now the conditions for producers and consumer to proceed is met
Again, you seem to assume that mask == size. When size is power of 2,
mask is calculated as size-1.