Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
};
like image 487
Steven Avatar asked Nov 05 '15 20:11

Steven


2 Answers

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 ];
    }
like image 53
mksteve Avatar answered Nov 14 '22 22:11

mksteve


You could just declare the mNextBufferIndex an std::atomic_ullong and then use

return mBuffers[(mNextBufferIndex++) % mTotalBuffers];

The increment will be atomic and you compute the modulo just before returning.

Using a very large unsigned will avoid problems that would occur when the counter wraps.

like image 41
6502 Avatar answered Nov 14 '22 23:11

6502