Don't over-allocate the active effect slot array
[openal-soft.git] / core / context.cpp
blobcff0e40cf39045d8069bdfd4df41c3e2f451c1bb
2 #include "config.h"
4 #include <cassert>
5 #include <functional>
6 #include <limits>
7 #include <memory>
8 #include <stdexcept>
9 #include <utility>
11 #include "async_event.h"
12 #include "context.h"
13 #include "device.h"
14 #include "effectslot.h"
15 #include "logging.h"
16 #include "ringbuffer.h"
17 #include "voice.h"
18 #include "voice_change.h"
21 #ifdef __cpp_lib_atomic_is_always_lock_free
22 static_assert(std::atomic<ContextBase::AsyncEventBitset>::is_always_lock_free, "atomic<bitset> isn't lock-free");
23 #endif
25 ContextBase::ContextBase(DeviceBase *device) : mDevice{device}
26 { assert(mEnabledEvts.is_lock_free()); }
28 ContextBase::~ContextBase()
30 mActiveAuxSlots.store(nullptr, std::memory_order_relaxed);
31 mVoices.store(nullptr, std::memory_order_relaxed);
33 if(mAsyncEvents)
35 size_t count{0};
36 auto evt_vec = mAsyncEvents->getReadVector();
37 if(evt_vec.first.len > 0)
39 std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf)),
40 evt_vec.first.len);
41 count += evt_vec.first.len;
43 if(evt_vec.second.len > 0)
45 std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)),
46 evt_vec.second.len);
47 count += evt_vec.second.len;
49 if(count > 0)
50 TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
51 mAsyncEvents->readAdvance(count);
56 void ContextBase::allocVoiceChanges()
58 static constexpr size_t clustersize{std::tuple_size_v<VoiceChangeCluster::element_type>};
60 VoiceChangeCluster clusterptr{std::make_unique<VoiceChangeCluster::element_type>()};
61 const auto cluster = al::span{*clusterptr};
63 for(size_t i{1};i < clustersize;++i)
64 cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
65 cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
67 mVoiceChangeClusters.emplace_back(std::move(clusterptr));
68 mVoiceChangeTail = mVoiceChangeClusters.back()->data();
71 void ContextBase::allocVoiceProps()
73 static constexpr size_t clustersize{std::tuple_size_v<VoicePropsCluster::element_type>};
75 TRACE("Increasing allocated voice properties to %zu\n",
76 (mVoicePropClusters.size()+1) * clustersize);
78 auto clusterptr = std::make_unique<VoicePropsCluster::element_type>();
79 auto cluster = al::span{*clusterptr};
80 for(size_t i{1};i < clustersize;++i)
81 cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
82 mVoicePropClusters.emplace_back(std::move(clusterptr));
84 VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)};
85 do {
86 mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed);
87 } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(),
88 std::memory_order_acq_rel, std::memory_order_acquire) == false);
91 void ContextBase::allocVoices(size_t addcount)
93 static constexpr size_t clustersize{std::tuple_size_v<VoiceCluster::element_type>};
94 /* Convert element count to cluster count. */
95 addcount = (addcount+(clustersize-1)) / clustersize;
97 if(!addcount)
99 if(!mVoiceClusters.empty())
100 return;
101 ++addcount;
104 if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
105 throw std::runtime_error{"Allocating too many voices"};
106 const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
107 TRACE("Increasing allocated voices to %zu\n", totalcount);
109 while(addcount)
111 mVoiceClusters.emplace_back(std::make_unique<VoiceCluster::element_type>());
112 --addcount;
115 auto newarray = VoiceArray::Create(totalcount);
116 auto voice_iter = newarray->begin();
117 for(VoiceCluster &cluster : mVoiceClusters)
118 voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter,
119 [](Voice &voice) noexcept -> Voice* { return &voice; });
121 if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel))
122 std::ignore = mDevice->waitForMix();
126 void ContextBase::allocEffectSlotProps()
128 static constexpr size_t clustersize{std::tuple_size_v<EffectSlotPropsCluster::element_type>};
130 TRACE("Increasing allocated effect slot properties to %zu\n",
131 (mEffectSlotPropClusters.size()+1) * clustersize);
133 auto clusterptr = std::make_unique<EffectSlotPropsCluster::element_type>();
134 auto cluster = al::span{*clusterptr};
135 for(size_t i{1};i < clustersize;++i)
136 cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
137 auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get();
139 EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)};
140 do {
141 newcluster->back().next.store(oldhead, std::memory_order_relaxed);
142 } while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(),
143 std::memory_order_acq_rel, std::memory_order_acquire) == false);
146 EffectSlot *ContextBase::getEffectSlot()
148 for(auto& clusterptr : mEffectSlotClusters)
150 const auto cluster = al::span{*clusterptr};
151 auto iter = std::find_if_not(cluster.begin(), cluster.end(),
152 std::mem_fn(&EffectSlot::InUse));
153 if(iter != cluster.end()) return al::to_address(iter);
156 auto clusterptr = std::make_unique<EffectSlotCluster::element_type>();
157 if(1 >= std::numeric_limits<int>::max()/clusterptr->size() - mEffectSlotClusters.size())
158 throw std::runtime_error{"Allocating too many effect slots"};
159 const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()};
160 TRACE("Increasing allocated effect slots to %zu\n", totalcount);
162 mEffectSlotClusters.emplace_back(std::move(clusterptr));
163 return mEffectSlotClusters.back()->data();
167 void ContextBase::allocContextProps()
169 static constexpr size_t clustersize{std::tuple_size_v<ContextPropsCluster::element_type>};
171 TRACE("Increasing allocated context properties to %zu\n",
172 (mContextPropClusters.size()+1) * clustersize);
174 auto clusterptr = std::make_unique<ContextPropsCluster::element_type>();
175 auto cluster = al::span{*clusterptr};
176 for(size_t i{1};i < clustersize;++i)
177 cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed);
178 auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get();
180 ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)};
181 do {
182 newcluster->back().next.store(oldhead, std::memory_order_relaxed);
183 } while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(),
184 std::memory_order_acq_rel, std::memory_order_acquire) == false);