diff options
-rw-r--r-- | Alc/alc.cpp | 127 | ||||
-rw-r--r-- | Alc/alcontext.h | 2 | ||||
-rw-r--r-- | Alc/alu.cpp | 12 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 2 |
4 files changed, 85 insertions, 58 deletions
diff --git a/Alc/alc.cpp b/Alc/alc.cpp index 0e395a89..e3eedbfa 100644 --- a/Alc/alc.cpp +++ b/Alc/alc.cpp @@ -879,6 +879,12 @@ constexpr ALCint alcEFXMajorVersion = 1; constexpr ALCint alcEFXMinorVersion = 0; +/* To avoid extraneous allocations, a 0-sized FlexArray<ALCcontext*> is defined + * globally as a sharable object. + */ +al::FlexArray<ALCcontext*> EmptyContextArray{0u}; + + /************************************************ * Device lists ************************************************/ @@ -1598,7 +1604,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) DevFmtType oldType; ALboolean update_failed; ALCsizei hrtf_id = -1; - ALCcontext *context; ALCuint oldFreq; int val; @@ -2105,8 +2110,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) */ update_failed = AL_FALSE; FPUCtl mixer_mode{}; - context = device->ContextList.load(); - while(context) + for(ALCcontext *context : *device->mContexts.load()) { if(context->DefaultSlot) { @@ -2246,8 +2250,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) context->Listener.PropsClean.test_and_set(std::memory_order_release); UpdateListenerProps(context); UpdateAllSourceProps(context); - - context = context->next.load(std::memory_order_relaxed); } mixer_mode.leave(); if(update_failed) @@ -2264,7 +2266,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) } -ALCdevice::ALCdevice(DeviceType type) : Type{type} +ALCdevice::ALCdevice(DeviceType type) : Type{type}, mContexts{&EmptyContextArray} { } @@ -2303,6 +2305,9 @@ ALCdevice::~ALCdevice() if(mHrtf) mHrtf->DecRef(); mHrtf = nullptr; + + auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed); + if(oldarray != &EmptyContextArray) delete oldarray; } @@ -2563,30 +2568,45 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device) if(GlobalContext.compare_exchange_strong(origctx, nullptr)) ALCcontext_DecRef(context); - bool ret{true}; - { BackendLockGuard _{*device->Backend}; - origctx = context; - ALCcontext *newhead{context->next.load(std::memory_order_relaxed)}; - if(!device->ContextList.compare_exchange_strong(origctx, newhead)) + bool ret{}; + { + using ContextArray = al::FlexArray<ALCcontext*>; + + /* First make sure this context exists in the device's list. */ + auto *oldarray = device->mContexts.load(std::memory_order_acquire); + if(auto toremove = std::count(oldarray->begin(), oldarray->end(), context)) { - ALCcontext *list; - do { - /* origctx is what the desired context failed to match. Try - * swapping out the next one in the list. - */ - list = origctx; - origctx = context; - } while(!list->next.compare_exchange_strong(origctx, newhead)); + auto alloc_ctx_array = [](const size_t count) -> ContextArray* + { + if(count == 0) return &EmptyContextArray; + void *ptr{al_calloc(alignof(ContextArray), ContextArray::Sizeof(count))}; + return new (ptr) ContextArray{count}; + }; + auto *newarray = alloc_ctx_array(oldarray->size() - toremove); + + /* Copy the current/old context handles to the new array, excluding + * the given context. + */ + std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(), + std::bind(std::not_equal_to<ALCcontext*>{}, _1, context)); + + /* Store the new context array in the device. Wait for any current + * mix to finish before deleting the old array. + */ + device->mContexts.store(newarray); + if(oldarray != &EmptyContextArray) + { + while((device->MixCount.load(std::memory_order_acquire)&1)) + std::this_thread::yield(); + delete oldarray; + } + + ret = !newarray->empty(); } else - ret = !!newhead; + ret = !oldarray->empty(); } - /* Make sure the context is finished and no longer processing in the mixer - * before sending the message queue kill event. The backend's lock does - * this, although waiting for a non-odd mix count would work too. - */ - StopEventThrd(context); ALCcontext_DecRef(context); @@ -3473,20 +3493,42 @@ START_API_FUNC UpdateListenerProps(context.get()); { + using ContextArray = al::FlexArray<ALCcontext*>; + + /* Allocate a new context array, which holds 1 more than the current/ + * old array. + */ + auto *oldarray = device->mContexts.load(); + const size_t newcount{oldarray->size()+1}; + void *ptr{al_calloc(alignof(ContextArray), ContextArray::Sizeof(newcount))}; + auto *newarray = new (ptr) ContextArray{newcount}; + + /* Copy the current/old context handles to the new array, appending the + * new context. + */ + auto iter = std::copy(oldarray->begin(), oldarray->end(), newarray->begin()); + *iter = context.get(); + + /* Store the new context array in the device. Wait for any current mix + * to finish before deleting the old array. + */ + dev->mContexts.store(newarray); + if(oldarray != &EmptyContextArray) { - std::lock_guard<std::recursive_mutex> _{ListLock}; - auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get()); - ContextList.insert(iter, context.get()); - ALCcontext_IncRef(context.get()); + while((dev->MixCount.load(std::memory_order_acquire)&1)) + std::this_thread::yield(); + delete oldarray; } - - ALCcontext *head = dev->ContextList.load(); - do { - context->next.store(head, std::memory_order_relaxed); - } while(!dev->ContextList.compare_exchange_weak(head, context.get())); } statelock.unlock(); + { + std::lock_guard<std::recursive_mutex> _{ListLock}; + auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get()); + ContextList.insert(iter, context.get()); + ALCcontext_IncRef(context.get()); + } + if(context->DefaultSlot) { if(InitializeEffect(context.get(), context->DefaultSlot.get(), &DefaultEffect) == AL_NO_ERROR) @@ -3867,24 +3909,19 @@ START_API_FUNC * respective lists. */ DeviceList.erase(iter); - ALCcontext *ctx{device->ContextList.load()}; - while(ctx != nullptr) + for(ALCcontext *ctx : *device->mContexts.load()) { - ALCcontext *next = ctx->next.load(std::memory_order_relaxed); auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), ctx); if(iter != ContextList.cend() && *iter == ctx) ContextList.erase(iter); - ctx = next; } listlock.unlock(); - ctx = device->ContextList.load(std::memory_order_relaxed); - while(ctx != nullptr) + al::FlexArray<ALCcontext*> *contexts; + while(!(contexts=device->mContexts.load(std::memory_order_relaxed))->empty()) { - ALCcontext *next = ctx->next.load(std::memory_order_relaxed); - WARN("Releasing context %p\n", ctx); - ReleaseContext(ctx, device); - ctx = next; + WARN("Releasing context %p\n", contexts->front()); + ReleaseContext(contexts->front(), device); } if(device->Flags.get<DeviceRunning>()) device->Backend->stop(); @@ -4221,7 +4258,7 @@ START_API_FUNC if(!dev->Flags.get<DevicePaused>()) return; dev->Flags.unset<DevicePaused>(); - if(dev->ContextList.load() == nullptr) + if(dev->mContexts.load()->empty()) return; if(dev->Backend->start() == ALC_FALSE) diff --git a/Alc/alcontext.h b/Alc/alcontext.h index 1887e341..0e3b864e 100644 --- a/Alc/alcontext.h +++ b/Alc/alcontext.h @@ -136,8 +136,6 @@ struct ALCcontext { ALCdevice *const Device; const ALCchar *ExtensionList{nullptr}; - std::atomic<ALCcontext*> next{nullptr}; - ALlistener Listener{}; diff --git a/Alc/alu.cpp b/Alc/alu.cpp index 846d9aea..094f0dc6 100644 --- a/Alc/alu.cpp +++ b/Alc/alu.cpp @@ -1658,14 +1658,9 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples) /* For each context on this device, process and mix its sources and * effects. */ - ALCcontext *ctx{device->ContextList.load(std::memory_order_acquire)}; - while(ctx) - { + for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire)) ProcessContext(ctx, SamplesToDo); - ctx = ctx->next.load(std::memory_order_relaxed); - } - /* 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 @@ -1754,8 +1749,7 @@ void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.user.msg)) evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0; - ALCcontext *ctx{device->ContextList.load()}; - while(ctx) + for(ALCcontext *ctx : *device->mContexts.load()) { const ALbitfieldSOFT enabledevt{ctx->EnabledEvts.load(std::memory_order_acquire)}; if((enabledevt&EventType_Disconnected)) @@ -1780,7 +1774,5 @@ void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) std::for_each(ctx->Voices->begin(), ctx->Voices->begin() + ctx->VoiceCount.load(std::memory_order_acquire), stop_voice); - - ctx = ctx->next.load(std::memory_order_relaxed); } } diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 80167417..016cc535 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -461,7 +461,7 @@ struct ALCdevice { RefCount MixCount{0u}; // Contexts created on this device - std::atomic<ALCcontext*> ContextList{nullptr}; + std::atomic<al::FlexArray<ALCcontext*>*> mContexts{nullptr}; /* This lock protects the device state (format, update size, etc) from * being from being changed in multiple threads, or being accessed while |