How should boost::lockfree::spsc_queue's read_available and write_available be used?

1k views Asked by At

Boost documentation for spsc_queue says:

read_available(): Thread-safe and wait-free, should only be called from the producer thread

write_available(): Thread-safe and wait-free, should only be called from the consumer thread

I would expect the most common use case to be just the other way around: producer thread (thread writing data to the queue) would need write_available(), and consumer thread (thread reading data from the queue) would need read_available().

If I need to know how much I can write to the queue in the producer thread, should I use QUEUE_CAPACITY - read_available()?

1

There are 1 answers

2
sehe On BEST ANSWER

Any kind of size assessment is going to be a race in the lockfree world.

Simple reason being that the size might be changed on other threads before you act on the "measured size".

Single-Producer/Single-Consumer is special in the sense that the consumer knows nobody else can read from the the queue (so "read_available" will never decrease unless the consumer reads it itself). Similarly for the producer side.

It is clear that write_available is what you need. Sure, it could be more by the time you actually write, but you can't get more accurate. At least it will never be less (after all there is only 1 producer thread).

Note that the documentation appears to be in error (swapping the allowed threads)

This made me double check, and sure enough they use the functions internally in the expected ways (contradicting the erronous documentation claim):

ConstIterator push(ConstIterator begin, ConstIterator end, T * internal_buffer, size_t max_size)
{
    const size_t write_index = write_index_.load(memory_order_relaxed);  // only written from push thread
    const size_t read_index  = read_index_.load(memory_order_acquire);
    const size_t avail = write_available(write_index, read_index, max_size);

I heartily suggest using the range-push shown here to automatically push the exact number of items possible. E.g.:

auto b = messages.begin(), e = messages.end();
do {
    b = a.push(b, e)
} while (b != e);