[LAD] Realtime inter-thread communication

Sebastian Gesemann s.gesemann at gmail.com
Wed Mar 2 13:21:12 UTC 2016


On Tue, Mar 1, 2016 at 6:40 PM, Paul Davis <paul at linuxaudiosystems.com> wrote:
> On Tue, Mar 1, 2016 at 12:09 PM, Sebastian Gesemann <s.gesemann at gmail.com>
> wrote:
>>
>> It depends on what meanings you attach to the words "atomics" and
>> "atomicity". I was trying to use the term "atomic" in a way consistent
>> with the C11/C++11 memory model. In this context, atomicity is not
>> only about having logically multiple operations done as a single one
>> (fetch-and-add, compare-and-swap, etc) but it also involves memory
>> ordering hints (defaulting to sequential constistency but weaker
>> models are possible). So, it seems to me that you were not familiar
>> with this. I said I have little experience with lock-free programming
>> but that does not mean I'm completely unaware of the theoretical
>> aspects.
>
> the evil that lock-free data structures seek to avoid is mutual exclusion
> that involves stopping thread execution. they do not require that things are
> atomic in either the weak or strong sense, but they do require that the data
> structures remain consistent and accurate from the POV of the threads that
> use them.

Exactly. Specifically, I would want to make sure that the consumer
thread sees the update to the write pointer *not before* the updates
to the buffer's memory. This was my concern because memory access with
three levels of partly shared caches between cores is more complicated
than possible instruction reorderings. But by now I have convinced
myself that this is guaranteed if the producer writes a new value to
write_pos with *release semantics* after updating the buffer and the
consumer loads the value of write_pos with *acquire semantics*.

http://en.cppreference.com/w/cpp/atomic/memory_order

> [...]
> the implementation inside Ardour uses glib's atomic wrappers to make the
> second assumption strong.
> https://github.com/Ardour/ardour/blob/master/libs/pbd/pbd/ringbuffer.h

Nice. Yeah, it seems glib's atomics make the strongest possible memory
ordering guarantee (more than you actually need in this ringbuffer
case).


@all: Thank you for your responses. I'm going to use my own ringbuffer
using C++11's std::atomics.


@Paul:
As for the rest of the code, I just noticed a couple of other things
you might be interested in:

lines 103-107 are

    if (w > r) {
        return w - r;
    } else {
        return (w - r + size) & size_mask;
    }

If I'm not miskaten, this could be simplified to

    return (unsigned)(w - r) & size_mask;

The "(unsigned)" isn't even necessary because of integral promotion,
but I'd prefer this to be explicit.

The class violates the "rule of three". It allows a user to
copy-construct as well as copy-assign such ringbuffer objects
resulting in double-free errors. I'd prefer to manually disable the
copy constructor and assignment operator. Right now these special
member functions can be generated by the compiler and would do the
wrong thing.

I'd mark the constructor as explicit to avoid accidental int-to-buffer
conversions (like std::vector).


Cheers!
sg


More information about the Linux-audio-dev mailing list