diff options
author | Chris Robinson <chris.kcat@gmail.com> | 2023-12-04 01:18:49 -0800 |
---|---|---|
committer | Chris Robinson <chris.kcat@gmail.com> | 2023-12-04 02:25:05 -0800 |
commit | e123e7bbda4330559ef03a5362bc93064eb87e4e (patch) | |
tree | 5dc77aeefd6ae7576f8cc8fb0b7c5a35ad7ee46d | |
parent | b6a68e8d510610e181d638ff993e327059bd6018 (diff) |
Use RAII to handle writing under the mixer seqlock
-rw-r--r-- | al/source.cpp | 6 | ||||
-rw-r--r-- | alc/alc.cpp | 8 | ||||
-rw-r--r-- | alc/alu.cpp | 28 | ||||
-rw-r--r-- | alc/backends/base.cpp | 2 | ||||
-rw-r--r-- | alc/backends/pipewire.cpp | 2 | ||||
-rw-r--r-- | core/device.h | 28 |
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; } |