Search code examples
c++multithreadingc++11atomic

C++ thread-safe increment with modulo without mutex using std::atomic


I need a thread-safe pool of Buffer-objects used in a round-robin fashion. I normally would just put a mutex in there to make the increment and modulo thread safe, but is it possible to write it using std::atomic? Here's a sample interface. If it makes things easier the total number of buffers can be a power of two. The next buffer index is never accessed outside of the class.

class Buffer;

class BufferManager
{
public:

    BufferManager( size_t totalBuffers = 8 ) : mNextBufferIndex( 0 ), mTotalBuffers( totalBuffers )
    {
        mBuffers = new Buffer*[mTotalBuffers];
    }

    Buffer* GetNextBuffer()
    {
        // How to make this operation atomic?
        size_t index = mNextBufferIndex; 

        mNextBufferIndex = ( mNextBufferIndex + 1 ) % mTotalBuffers;

        return mBuffers[index];
    }

private:
    Buffer**            mBuffers;
    size_t              mNextBufferIndex;
    size_t              mTotalBuffers;
};

Solution

  • Modulo can be safely used after choosing

    std::atomic<size_t> mNextBufferIndex;
    
        Buffer* GetNextBuffer()
        {
           // How to make this operation atomic?
           size_t index = mNextBufferIndex ++; 
    
           size_t id = index % mTotalBuffers;
    
           // If size could wrap, then re-write the modulo value.
           // oldValue keeps getting re-read.  
           // modulo occurs when nothing else updates it.
           size_t oldValue =mNextBufferIndex;
           size_t newValue = oldValue % mTotalBuffers;
           while (!m_mNextBufferIndex.compare_exchange_weak( oldValue, newValue, std::memory_order_relaxed ) )
                newValue = oldValue % mTotalBuffers; 
           return mBuffers[id ];
        }