HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Lockless, blocking, non synchronized multiple producers and consumers ring buffer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
buffernonblockingsynchronizedmultipleandproducerslocklessringconsumers

Problem

My program is structured like this: there are M producer threads, each of which computes a section of an object O with index i O[i]. When a specific O[i] is completed by all producers, it is put in a ring buffer, which is consumed by N threads. When all consumers are done with a specific O[i] can be discarded.

There must be no intra-group synchronization, that is producers need not to synchronize work on a single O[i], as well as consumers. The objects must appear in order in the ring though. Also, the speed of production and consumption can vary wildly, so a consumer that cannot acquire an element from the ring must block, and if a producer is about to start working on an element but the ring is full, it shall block. In all the other cases, insertion and removal should be lock free.

Here's what I came up with C++11. I seek for advice especially on the usage of the various relaxed memory models. I have checked the assembly and my code seems not to produce any mfence instruction on x86, only some lock when incrementing reference counting. Is that correct and expected?

```
#ifndef MULTIQUEUE_HPP_
#define MULTIQUEUE_HPP_

#include
#include
#include
#include

template
class multiqueue_cacheline{

const int inthread, outthread;
const size_t ringsize, unblockproducer, unblockconsumer;

struct ref{
std::atomic v;
bool eof;
char padding[linesize > sizeof(v) + sizeof(eof) ? linesize - sizeof(v) - sizeof(eof) : 0];
ref(int v): v(v), eof(false) {}
ref(const ref& o): v(o.v.load(std::memory_order_relaxed)), eof(false) {}
};

std::vector refs;
std::atomic inring = {0};
struct sleepobj{
std::condition_variable cond;
std::mutex m;
template
void sleep(T& ready){
std::unique_lock lk(m);
if(!ready()) cond.wait(lk, ready);
}
void wake(){
std::unique_lock lk(m);
cond.notify_all();
}
} co

Solution

-
You are trying to align each ref object to a cache line by padding the object to fill an entire cache line. However your refs vector is embedded as a member in the multiqueue class hence it will start at an offset relative to the beginning of the object which is probably either aligned on a 4 or 8 byte boundary. So your refs vector might start in the middle of a cache line and your padding to cache line size ill not gain you what you wanted.

Aligning a member within a class to a cacheline seems to be a non trivial exercise. This question on SO might be helpful.

-
Your code is really hard to understand from just reading mostly due to not very helpful names for parameters to the class. It's not very obvious what the inthread, outthread, unblockconsumer and unblockproducer parameters mean and in how far they relate to the workings of the queue. I assume inthread is numberOfProducers and outthread is numberOfConsumers - the others are bit more convoluted. Adding some documentation comments would help.

Context

StackExchange Code Review Q#65292, answer score: 3

Revisions (0)

No revisions yet.