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