#include "config.h" #include #include #include #include #include #include "async_event.h" #include "context.h" #include "device.h" #include "effectslot.h" #include "logging.h" #include "ringbuffer.h" #include "voice.h" #include "voice_change.h" #ifdef __cpp_lib_atomic_is_always_lock_free static_assert(std::atomic::is_always_lock_free, "atomic isn't lock-free"); #endif ContextBase::ContextBase(DeviceBase *device) : mDevice{device} { assert(mEnabledEvts.is_lock_free()); } ContextBase::~ContextBase() { size_t count{0}; ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; if(cprops) { ++count; delete cprops; } cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); while(cprops) { std::unique_ptr old{cprops}; cprops = old->next.load(std::memory_order_relaxed); ++count; } TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); count = 0; EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; while(eprops) { std::unique_ptr old{eprops}; eprops = old->next.load(std::memory_order_relaxed); ++count; } TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) { std::destroy_n(curarray->end(), curarray->size()); delete curarray; } delete mVoices.exchange(nullptr, std::memory_order_relaxed); if(mAsyncEvents) { count = 0; auto evt_vec = mAsyncEvents->getReadVector(); if(evt_vec.first.len > 0) { std::destroy_n(reinterpret_cast(evt_vec.first.buf), evt_vec.first.len); count += evt_vec.first.len; } if(evt_vec.second.len > 0) { std::destroy_n(reinterpret_cast(evt_vec.second.buf), evt_vec.second.len); count += evt_vec.second.len; } if(count > 0) TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s"); mAsyncEvents->readAdvance(count); } } void ContextBase::allocVoiceChanges() { constexpr size_t clustersize{128}; VoiceChangeCluster cluster{std::make_unique(clustersize)}; for(size_t i{1};i < clustersize;++i) cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); mVoiceChangeClusters.emplace_back(std::move(cluster)); mVoiceChangeTail = mVoiceChangeClusters.back().get(); } void ContextBase::allocVoiceProps() { constexpr size_t clustersize{32}; TRACE("Increasing allocated voice properties to %zu\n", (mVoicePropClusters.size()+1) * clustersize); VoicePropsCluster cluster{std::make_unique(clustersize)}; for(size_t i{1};i < clustersize;++i) cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); mVoicePropClusters.emplace_back(std::move(cluster)); VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)}; do { mVoicePropClusters.back()[clustersize-1].next.store(oldhead, std::memory_order_relaxed); } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back().get(), std::memory_order_acq_rel, std::memory_order_acquire) == false); } void ContextBase::allocVoices(size_t addcount) { constexpr size_t clustersize{32}; /* Convert element count to cluster count. */ addcount = (addcount+(clustersize-1)) / clustersize; if(addcount >= std::numeric_limits::max()/clustersize - mVoiceClusters.size()) throw std::runtime_error{"Allocating too many voices"}; const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; TRACE("Increasing allocated voices to %zu\n", totalcount); auto newarray = VoiceArray::Create(totalcount); while(addcount) { mVoiceClusters.emplace_back(std::make_unique(clustersize)); --addcount; } auto voice_iter = newarray->begin(); for(VoiceCluster &cluster : mVoiceClusters) { for(size_t i{0};i < clustersize;++i) *(voice_iter++) = &cluster[i]; } if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) { mDevice->waitForMix(); delete oldvoices; } } EffectSlot *ContextBase::getEffectSlot() { for(auto& cluster : mEffectSlotClusters) { for(size_t i{0};i < EffectSlotClusterSize;++i) { if(!cluster[i].InUse) return &cluster[i]; } } if(1 >= std::numeric_limits::max()/EffectSlotClusterSize - mEffectSlotClusters.size()) throw std::runtime_error{"Allocating too many effect slots"}; const size_t totalcount{(mEffectSlotClusters.size()+1) * EffectSlotClusterSize}; TRACE("Increasing allocated effect slots to %zu\n", totalcount); mEffectSlotClusters.emplace_back(std::make_unique(EffectSlotClusterSize)); return getEffectSlot(); }