Sample ring buffer that allows read/write operations between two threads without a lock.
nn::nlib::threading::SimpleRingBuffer
is a class template. It is written to specify the type T
to store in the ring buffer, and the size of the ring buffer N
as a template argument.
In the case of a 1:1 thread, using this class allows for data to be exchanged safely without using a lock.
This sample shows a thread generating integers from 0 to 99, and pushing it to the ring buffer.
The main thread pops a value from the ring buffer 100 times, and checks whether the values are in the correct order.
The SimpleRingBuffer
class uses the NLIB_MEMORY_ORDER_ACQUIRE
and NLIB_MEMORY_ORDER_RELEASE
macros for the memory barrier.
These macros are replaced with atomic_thread_fence(memory_order_acquire)
and atomic_thread_fence(memory_order_release)
respectively in a C++11 environment.
This function is replaced with equivalent code in C++03 or C.
using ::nlib_ns::threading::Thread;
template <class T, size_t N>
public:
SimpleRingBuffer() : head_(0), tail_(0) {}
bool TryPush(const T& value);
bool TryPop(T* value);
private:
uint32_t next(uint32_t current) { return (current + 1) % N; }
int32_t head_;
int32_t tail_;
T ring_[N];
};
template <class T, size_t N>
bool SimpleRingBuffer<T, N>::TryPush(const T& value) {
int32_t next_head = this->next(head);
if (next_head == tail) return false;
ring_[head] = value;
return true;
}
template <class T, size_t N>
bool SimpleRingBuffer<T, N>::TryPop(T* value) {
if (!value) return false;
if (tail == head) return false;
*value = ring_[tail];
return true;
}
SimpleRingBuffer<int, 10> g_Buffer;
Thread g_th;
void ThreadFunc() {
for (int i = 0; i < 100; ++i) {
while (!g_Buffer.TryPush(i)) {
}
}
}
bool SimpleRingBufferDemo() {
nlib_printf(
"One thread pushes [0...99] into the ring buffer,\n");
nlib_printf(
"while the other thread pops numbers from the same buffer without any lock.\n");
nlib_printf(
"This works well with memory barrier.\n\n");
g_th.Start(ThreadFunc);
g_th.Detach();
for (int i = 0; i < 100; ++i) {
int val;
while (!g_Buffer.TryPop(&val)) {
}
if (val != i) return false;
}
return true;
}
static bool SampleMain(int, char**) { return SimpleRingBufferDemo(); }
NLIB_MAINFUNC