1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-11 09:31:13 +02:00

Define utility class MessageBuffer for inter-thread messages

This commit is contained in:
Paul Licameli 2018-08-12 11:07:45 -04:00
parent 976e9aeec7
commit 0c0cc07686

View File

@ -198,6 +198,109 @@ int audacityAudioCallback(
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData ); PaStreamCallbackFlags statusFlags, void *userData );
// Communicate data from one writer to one reader.
// This is not a queue: it is not necessary for each write to be read.
// Rather loss of a message is allowed: writer may overwrite.
// Data must be default-constructible and either copyable or movable.
template<typename Data>
class MessageBuffer {
struct alignas( 64
//std::hardware_destructive_interference_size // C++17
) UpdateSlot {
std::atomic<bool> mBusy{ false };
Data mData;
} mSlots[2];
std::atomic<unsigned char> mLastWrittenSlot{ 0 };
public:
void Initialize();
// Move data out (if available), or else copy it out
Data Read();
// Copy data in
void Write( const Data &data );
// Move data in
void Write( Data &&data );
};
template<typename Data>
void MessageBuffer<Data>::Initialize()
{
for (auto &slot : mSlots)
// Lock both slots first, maybe spinning a little
while ( slot.mBusy.exchange( true, std::memory_order_acquire ) )
{}
mSlots[0].mData = {};
mSlots[1].mData = {};
mLastWrittenSlot.store( 0, std::memory_order_relaxed );
for (auto &slot : mSlots)
slot.mBusy.exchange( false, std::memory_order_release );
}
template<typename Data>
Data MessageBuffer<Data>::Read()
{
// Whichever slot was last written, prefer to read that.
auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
idx = 1 - idx;
bool wasBusy = false;
do {
// This loop is unlikely to execute twice, but it might because the
// producer thread is writing a slot.
idx = 1 - idx;
wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
} while ( wasBusy );
// Copy the slot
auto result = std::move( mSlots[idx].mData );
mSlots[idx].mBusy.store( false, std::memory_order_release );
return result;
}
template<typename Data>
void MessageBuffer<Data>::Write( const Data &data )
{
// Whichever slot was last written, prefer to write the other.
auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
bool wasBusy = false;
do {
// This loop is unlikely to execute twice, but it might because the
// consumer thread is reading a slot.
idx = 1 - idx;
wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
} while ( wasBusy );
mSlots[idx].mData = data;
mLastWrittenSlot.store( idx, std::memory_order_relaxed );
mSlots[idx].mBusy.store( false, std::memory_order_release );
}
template<typename Data>
void MessageBuffer<Data>::Write( Data &&data )
{
// Whichever slot was last written, prefer to write the other.
auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
bool wasBusy = false;
do {
// This loop is unlikely to execute twice, but it might because the
// consumer thread is reading a slot.
idx = 1 - idx;
wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
} while ( wasBusy );
mSlots[idx].mData = std::move( data );
mLastWrittenSlot.store( idx, std::memory_order_relaxed );
mSlots[idx].mBusy.store( false, std::memory_order_release );
}
class AUDACITY_DLL_API AudioIO final { class AUDACITY_DLL_API AudioIO final {
public: public: