File: LockFreeQueue.h

package info (click to toggle)
audacity 3.7.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 125,252 kB
  • sloc: cpp: 358,238; ansic: 75,458; lisp: 7,761; sh: 3,410; python: 1,503; xml: 1,385; perl: 854; makefile: 122
file content (96 lines) | stat: -rw-r--r-- 2,435 bytes parent folder | download | duplicates (2)
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;
}