1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************
Audacity: A Digital Audio Editor
LockFreeQueue.h
Matthieu Hodgkinson
Moved from MeterPanel.h
**********************************************************************/
#pragma once
#include "MemoryX.h"
#include <atomic>
#include <cassert>
// Single-producer, single-consumer thread-safe queue of update messages
template <typename T>
class LockFreeQueue : public SharedNonInterfering<LockFreeQueue<T>>
{
public:
explicit LockFreeQueue(size_t maxLen);
~LockFreeQueue();
bool Put(const T& msg);
bool Get(T& msg);
void Clear();
private:
// Align the two atomics to avoid false sharing
// mStart is written only by the reader, mEnd by the writer
NonInterfering<std::atomic<size_t>> mStart { 0 }, mEnd { 0 };
const size_t mBufferSize;
ArrayOf<T> mBuffer { mBufferSize };
};
template <typename T>
LockFreeQueue<T>::LockFreeQueue(size_t maxLen)
: mBufferSize(maxLen)
{
Clear();
}
// destructor
template <typename T> LockFreeQueue<T>::~LockFreeQueue()
{
}
template <typename T> void LockFreeQueue<T>::Clear()
{
mStart.store(0);
mEnd.store(0);
}
// Add a message to the end of the queue. Return false if the
// queue was full.
template <typename T> bool LockFreeQueue<T>::Put(const T& msg)
{
auto start = mStart.load(std::memory_order_acquire);
auto end = mEnd.load(std::memory_order_relaxed);
// mStart can be greater than mEnd because it is all mod mBufferSize
assert((end + mBufferSize - start) >= 0);
int len = (end + mBufferSize - start) % mBufferSize;
// Never completely fill the queue, because then the
// state is ambiguous (mStart==mEnd)
if (len + 1 >= (int)(mBufferSize))
return false;
// wxLogDebug(wxT("Put: %s"), msg.toString());
mBuffer[end] = msg;
mEnd.store((end + 1) % mBufferSize, std::memory_order_release);
return true;
}
// Get the next message from the start of the queue.
// Return false if the queue was empty.
template <typename T> bool LockFreeQueue<T>::Get(T& msg)
{
auto start = mStart.load(std::memory_order_relaxed);
auto end = mEnd.load(std::memory_order_acquire);
int len = (end + mBufferSize - start) % mBufferSize;
if (len == 0)
return false;
msg = mBuffer[start];
mStart.store((start + 1) % mBufferSize, std::memory_order_release);
return true;
}
|