Буфер за звънене без изчакване

Имам сценарий, при който мога да имам множество производители, но един потребител на пръстенов буфер. ето изходния код:

typedef struct ring_buffer_t {
    uint8_t *data;
    uint32_t element_size;
    uint32_t element_count;
    uint32_t head;
    uint32_t tail;
    uint32_t mask;
} ring_buffer;

uint32_t ring_buffer_put(ring_buffer *buf, void *data_element) {
    int i;
    uint32_t status = 0;
    uint8_t *buf_pointer;
    uint8_t *element = (uint8_t *) data_element;

    if (buf && data_element) {

        buf_pointer = &buf->data[(buf->head & buf->mask) * buf->element_size];
        for (i = 0; i < buf->element_size; ++i) {
            buf_pointer[i] = element[i];
        }

        status = 1;
        __sync_fetch_and_add(&buf->head, 1);

    }

    return status;
}

uint32_t ring_buffer_size(ring_buffer *buf) {

    return buf->head - buf->tail;
}

uint32_t ring_buffer_empty(ring_buffer *buf) {

    return (ring_buffer_size(buf) == 0);
}

void *ring_buffer_get(ring_buffer *buf) {
    void *element;
    //preserve the invariant that tail is always <= head
    if (ring_buffer_empty(buf)) {
        return 0;
    }

    element = &buf->data[(buf->tail & buf->mask) * buf->element_size];
    __sync_fetch_and_add(&buf->tail, 1);
    return element;
}

Изявленията за проблеми ми позволяват да презаписвам по-стари записи, напр. затова използвам и и няма проверка дали буферът е пълен при поставяне - просто презаписвам най-стария запис. Въпреки това ми е трудно да разсъждавам дали това е безопасно за нишки. Както беше посочено по-рано - важно е това да е без изчакване (съответно без заключване по дефиниция), тъй като производителите не могат да си позволят да блокират. Размерът винаги ще бъде степен на 2, така че & по време на индексирането работи.

Всеки принос ще бъде оценен?


person LordDoskias    schedule 22.11.2013    source източник
comment
Не виждам как може да бъде безопасен за нишки, __sync_fetch_and_add наистина е атомен, но какво предотвратява условието за състезание? Не го виждам.   -  person jim mcnamara    schedule 22.11.2013
comment
Единствената реализация на пръстенния буфер без изчакване е тази, която извиква exit(1), когато буферът се препълни.   -  person Hans Passant    schedule 22.11.2013
comment
Технически, буферът никога не може да се напълни, защото е напълно ОК да презаписвате стари записи.   -  person LordDoskias    schedule 22.11.2013


Отговори (1)


Не, внедряването ви не е безопасно за нишки.

Ако две нишки put елементи едновременно, те могат да се окажат записани в една и съща позиция или дори половината запис в една позиция, а другата половина в следващата позиция.

person Klas Lindbäck    schedule 22.11.2013
comment
Наистина не е безопасна нишката за случай на много производители, един потребител. Въпреки че ще работи за един потребител, един производител. За да го направя безопасен за нишки за SCMP, ще трябва да прибегна до нещо подобно на това, което прави disruptor blog.codeaholics.org/2011/the-disruptor-lock-free-publishing - person LordDoskias; 22.11.2013