c++ threadsafe ringbuffer implementation

Avb Avb picture Avb Avb · Sep 27, 2013 · Viewed 9.7k times · Source

I am doing multithread programming in C++ and I am wondering whether there is a thread-safe implementation of ringbuffer in C++ or do you have any idea how I can implement it.

Answer

Chad picture Chad · Sep 27, 2013

Here's a basic implementation. Requires objects stored in the buffer to be default constructable, and copyable (by storing them in a std::vector<>). Requires C++11 support (for std::atomic). Most any recent version of gcc will have it with -std=c++11 or -std=c++0x

If c++11 isn't available, substitute the appropriate compiler intrinsic for making head_ and tail_ atomic.

Should be safe for one reader thread and one writer thread.

Publish items by calling:

  auto val = ringbuffer.back();
  val = some_value;
  ringbuffer.push();

Retreive items by calling:

  auto val = ringbuffer.front();
  // do stuff with val
  ringbuffer.pop();

If back() returns a nullptr, then the buffer is "full". If front() returns a nullptr then the buffer is "empty".

Warning, not tested (at all) :D

  #include <vector>

  template <class T>
  class RingBuffer
  {
  public:
     RingBuffer(size_t buffer_size)
        : ring_(buffer_size)
        , buffer_size_(buffer_size)
        , head_(0)
        , tail_(0)
     {
     }

     T* back()
     {
        bool received = false;

        if(available(head_, tail_))
        {
           return &(ring_[head_ % buffer_size_]);
        }

        return nullptr;
     }

     void push()
     {
        ++head_;
     }

     T* front()
     {
        if(tail_ < head_)
        {
           return & ring_[tail_ % buffer_size_];
        }

        return nullptr;
     }

     void pop()
     {
        ++tail_;
     }

     size_t size() const
     {
        if(tail_ < head_)
           return buffer_size_ - ((tail_ + buffer_size_) - head_);
        else if(tail_ > head_)
           return buffer_size_ - (tail_ - head_);

        return 0;
     }

     bool available()
     {
        return available(head_, tail_);
     }

  private:
     bool available(uint64_t h, uint64_t t) const
     {
        if(h == t)
           return true;
        else if(t > h)
           return (t - h) > buffer_size_;
        else// if(h > t)
           return (t + buffer_size_) - h > 0;
     }

     std::vector<T> ring_;
     const size_t   buffer_size_;
     std::atomic<uint64_t> head_;
     std::atomic<uint64_t> tail_;
  };