Audacity 3.2.0
MessageBuffer.h
Go to the documentation of this file.
1/*!********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file MessageBuffer.h
6
7 Paul Licameli split from PlaybackSchedule.h
8
9 **********************************************************************/
10
11#ifndef __AUDACITY_MESSAGE_BUFFER__
12#define __AUDACITY_MESSAGE_BUFFER__
13
14#include <atomic>
15
17
22template<typename Data>
24 struct UpdateSlot {
25 Data mData;
26 std::atomic<bool> mBusy{ false };
27 };
29
30 std::atomic<unsigned char> mLastWrittenSlot{ 0 };
31
32public:
33 void Initialize();
34
36
39 template<typename Result = Data, typename... ConstructorArgs>
40 Result Read(ConstructorArgs &&...args);
41
43 template<typename Arg = Data&&> void Write( Arg &&arg );
44};
45
46template<typename Data>
48{
49 for (auto &slot : mSlots)
50 // Lock both slots first, maybe spinning a little
51 while ( slot.mBusy.exchange( true, std::memory_order_acquire ) )
52 {}
53
54 mSlots[0].mData = {};
55 mSlots[1].mData = {};
56 mLastWrittenSlot.store( 0, std::memory_order_relaxed );
57
58 for (auto &slot : mSlots)
59 slot.mBusy.exchange( false, std::memory_order_release );
60}
61
62template<typename Data>
63template<typename Result, typename... ConstructorArgs>
64Result MessageBuffer<Data>::Read(ConstructorArgs &&...args)
65{
66 // Whichever slot was last written, prefer to read that.
67 auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
68 idx = 1 - idx;
69 bool wasBusy = false;
70 do {
71 // This loop is unlikely to execute twice, but it might because the
72 // producer thread is writing a slot.
73 idx = 1 - idx;
74 wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
75 } while ( wasBusy );
76
77 // Copy the slot
78 Result result(
79 std::move( mSlots[idx].mData ), std::forward<ConstructorArgs>(args)... );
80
81 mSlots[idx].mBusy.store( false, std::memory_order_release );
82
83 return result;
84}
85
86template<typename Data>
87template<typename Arg>
89{
90 // Whichever slot was last written, prefer to write the other.
91 auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
92 bool wasBusy = false;
93 do {
94 // This loop is unlikely to execute twice, but it might because the
95 // consumer thread is reading a slot.
96 idx = 1 - idx;
97 wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
98 } while ( wasBusy );
99
100 mSlots[idx].mData = std::forward<Arg>(arg);
101 mLastWrittenSlot.store( idx, std::memory_order_relaxed );
102
103 mSlots[idx].mBusy.store( false, std::memory_order_release );
104}
105
106#endif
Communicate data atomically from one writer thread to one reader.
Definition: MessageBuffer.h:23
Result Read(ConstructorArgs &&...args)
Move data out (if available), or else copy it out.
Definition: MessageBuffer.h:64
void Initialize()
Definition: MessageBuffer.h:47
NonInterfering< UpdateSlot > mSlots[2]
Definition: MessageBuffer.h:28
void Write(Arg &&arg)
Reassign a slot by move or copy.
Definition: MessageBuffer.h:88
std::atomic< unsigned char > mLastWrittenSlot
Definition: MessageBuffer.h:30
std::atomic< bool > mBusy
Definition: MessageBuffer.h:26