aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Alc/alc.cpp127
-rw-r--r--Alc/alcontext.h2
-rw-r--r--Alc/alu.cpp12
-rw-r--r--OpenAL32/Include/alMain.h2
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