aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <chris.kcat@gmail.com>2023-12-04 01:18:49 -0800
committerChris Robinson <chris.kcat@gmail.com>2023-12-04 02:25:05 -0800
commite123e7bbda4330559ef03a5362bc93064eb87e4e (patch)
tree5dc77aeefd6ae7576f8cc8fb0b7c5a35ad7ee46d
parentb6a68e8d510610e181d638ff993e327059bd6018 (diff)
Use RAII to handle writing under the mixer seqlock
-rw-r--r--al/source.cpp6
-rw-r--r--alc/alc.cpp8
-rw-r--r--alc/alu.cpp28
-rw-r--r--alc/backends/base.cpp2
-rw-r--r--alc/backends/pipewire.cpp2
-rw-r--r--core/device.h28
6 files changed, 42 insertions, 32 deletions
diff --git a/al/source.cpp b/al/source.cpp
index cb24c09f..c9ec8f21 100644
--- a/al/source.cpp
+++ b/al/source.cpp
@@ -212,7 +212,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds
readPos += voice->mPositionFrac.load(std::memory_order_relaxed);
}
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != device->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != device->mMixCount.load(std::memory_order_relaxed));
if(!voice)
return 0;
@@ -252,7 +252,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl
readPos += voice->mPositionFrac.load(std::memory_order_relaxed);
}
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != device->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != device->mMixCount.load(std::memory_order_relaxed));
if(!voice)
return 0.0f;
@@ -302,7 +302,7 @@ NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed);
}
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != device->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != device->mMixCount.load(std::memory_order_relaxed));
if(!voice)
return T{0};
diff --git a/alc/alc.cpp b/alc/alc.cpp
index d974777b..9a919032 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -976,9 +976,7 @@ std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const f
*/
inline void UpdateClockBase(ALCdevice *device)
{
- const auto mixCount = device->MixCount.load(std::memory_order_relaxed);
- device->MixCount.store(mixCount+1, std::memory_order_relaxed);
- std::atomic_thread_fence(std::memory_order_release);
+ const auto mixLock = device->getWriteMixLock();
auto samplesDone = device->mSamplesDone.load(std::memory_order_relaxed);
auto clockBase = device->mClockBase.load(std::memory_order_relaxed);
@@ -986,8 +984,6 @@ inline void UpdateClockBase(ALCdevice *device)
clockBase += nanoseconds{seconds{samplesDone}} / device->Frequency;
device->mClockBase.store(clockBase, std::memory_order_relaxed);
device->mSamplesDone.store(0, std::memory_order_relaxed);
-
- device->MixCount.store(mixCount+2, std::memory_order_release);
}
/**
@@ -2515,7 +2511,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
basecount = dev->mClockBase.load(std::memory_order_relaxed);
samplecount = dev->mSamplesDone.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != dev->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != dev->mMixCount.load(std::memory_order_relaxed));
basecount += nanoseconds{seconds{samplecount}} / dev->Frequency;
*values = basecount.count();
}
diff --git a/alc/alu.cpp b/alc/alu.cpp
index 23518fa9..2bc648bf 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -2135,19 +2135,16 @@ uint DeviceBase::renderSamples(const uint numSamples)
for(FloatBufferLine &buffer : MixBuffer)
buffer.fill(0.0f);
- /* Increment the mix count at the start (lsb should now be 1). */
- const auto mixCount = MixCount.load(std::memory_order_relaxed);
- MixCount.store(mixCount+1, std::memory_order_relaxed);
- std::atomic_thread_fence(std::memory_order_release);
+ {
+ const auto mixLock = getWriteMixLock();
- /* Process and mix each context's sources and effects. */
- ProcessContexts(this, samplesToDo);
+ /* Process and mix each context's sources and effects. */
+ ProcessContexts(this, samplesToDo);
- /* Increment the clock time. Every second's worth of samples is converted
- * and added to clock base so that large sample counts don't overflow
- * during conversion. This also guarantees a stable conversion.
- */
- {
+ /* Every second's worth of samples is converted and added to clock base
+ * so that large sample counts don't overflow during conversion. This
+ * also guarantees a stable conversion.
+ */
auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo;
auto clockBase = mClockBase.load(std::memory_order_relaxed) +
std::chrono::seconds{samplesDone/Frequency};
@@ -2155,9 +2152,6 @@ uint DeviceBase::renderSamples(const uint numSamples)
mClockBase.store(clockBase, std::memory_order_relaxed);
}
- /* Increment the mix count at the end (lsb should now be 0). */
- MixCount.store(mixCount+2, std::memory_order_release);
-
/* Apply any needed post-process for finalizing the Dry mix to the RealOut
* (Ambisonic decode, UHJ encode, etc).
*/
@@ -2232,9 +2226,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz
void DeviceBase::handleDisconnect(const char *msg, ...)
{
- const auto mixCount = MixCount.load(std::memory_order_relaxed);
- MixCount.store(mixCount+1, std::memory_order_relaxed);
- std::atomic_thread_fence(std::memory_order_release);
+ const auto mixLock = getWriteMixLock();
if(Connected.exchange(false, std::memory_order_acq_rel))
{
@@ -2277,6 +2269,4 @@ void DeviceBase::handleDisconnect(const char *msg, ...)
std::for_each(voicelist.begin(), voicelist.end(), stop_voice);
}
}
-
- MixCount.store(mixCount+2, std::memory_order_release);
}
diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp
index b2b567a6..1677ae19 100644
--- a/alc/backends/base.cpp
+++ b/alc/backends/base.cpp
@@ -52,7 +52,7 @@ ClockLatency BackendBase::getClockLatency()
refcount = mDevice->waitForMix();
ret.ClockTime = mDevice->getClockTime();
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != mDevice->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed));
/* NOTE: The device will generally have about all but one periods filled at
* any given time during playback. Without a more accurate measurement from
diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp
index 754feb6c..adf9d62a 100644
--- a/alc/backends/pipewire.cpp
+++ b/alc/backends/pipewire.cpp
@@ -1827,7 +1827,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
mixtime = mDevice->getClockTime();
clock_gettime(CLOCK_MONOTONIC, &tspec);
std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != mDevice->MixCount.load(std::memory_order_relaxed));
+ } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed));
/* Convert the monotonic clock, stream ticks, and stream delay to
* nanoseconds.
diff --git a/core/device.h b/core/device.h
index 1f3c5105..1ac01ba6 100644
--- a/core/device.h
+++ b/core/device.h
@@ -284,7 +284,7 @@ struct DeviceBase {
* the end, so the bottom bit indicates if the device is currently mixing
* and the upper bits indicates how many mixes have been done.
*/
- std::atomic<uint> MixCount{0u};
+ std::atomic<uint> mMixCount{0u};
// Contexts created on this device
std::atomic<al::FlexArray<ContextBase*>*> mContexts{nullptr};
@@ -299,10 +299,34 @@ struct DeviceBase {
uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); }
+ struct MixLock {
+ std::atomic<uint> &mCount;
+ const uint mLastVal;
+
+ MixLock(std::atomic<uint> &count, const uint last_val) noexcept
+ : mCount{count}, mLastVal{last_val}
+ { }
+ /* Increment the mix count when the lock goes out of scope to "release"
+ * it (lsb should be 0).
+ */
+ ~MixLock() { mCount.store(mLastVal+2, std::memory_order_release); }
+ };
+ auto getWriteMixLock() noexcept
+ {
+ /* Increment the mix count at the start of mixing and writing clock
+ * info (lsb should be 1).
+ */
+ const auto mixCount = mMixCount.load(std::memory_order_relaxed);
+ mMixCount.store(mixCount+1, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_release);
+ return MixLock{mMixCount, mixCount};
+ }
+
+ /** Waits for the mixer to not be mixing or updating the clock. */
uint waitForMix() const noexcept
{
uint refcount;
- while((refcount=MixCount.load(std::memory_order_acquire))&1) {
+ while((refcount=mMixCount.load(std::memory_order_acquire))&1) {
}
return refcount;
}