aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2016-05-11 18:40:17 -0700
committerChris Robinson <[email protected]>2016-05-11 21:02:11 -0700
commit186b54aa3d5f1398a384fa318aa000210d82437e (patch)
tree32daef17365aee1fb61a05db9b6c23aec25f6e07 /Alc
parent21bc0f5ef8f0e410ea840061589b844d6e401afc (diff)
Use a lockless method for updating listener and context properties
This uses a separate container to provide the relevant properties to the internal update method, using atomic pointer swaps. A free-list is used to avoid having too many individual containers. This allows the mixer to update the internal listener properties without requiring the lock to protect against async updates. It also allows concurrent read access to the user-facing property values, even the multi-value ones (e.g. the vectors).
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c35
-rw-r--r--Alc/ALu.c58
2 files changed, 71 insertions, 22 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index b8dae07c..058dd242 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -2064,7 +2064,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
ALsizei pos;
- ATOMIC_STORE(&context->UpdateSources, AL_FALSE);
LockUIntMapRead(&context->EffectSlotMap);
for(pos = 0;pos < context->EffectSlotMap.size;pos++)
{
@@ -2272,11 +2271,16 @@ static ALCboolean VerifyDevice(ALCdevice **device)
static ALvoid InitContext(ALCcontext *Context)
{
ALlistener *listener = Context->Listener;
+
//Initialise listener
listener->Gain = 1.0f;
listener->MetersPerUnit = 1.0f;
- aluVectorSet(&listener->Position, 0.0f, 0.0f, 0.0f, 1.0f);
- aluVectorSet(&listener->Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+ listener->Position[0] = 0.0f;
+ listener->Position[1] = 0.0f;
+ listener->Position[2] = 0.0f;
+ listener->Velocity[0] = 0.0f;
+ listener->Velocity[1] = 0.0f;
+ listener->Velocity[2] = 0.0f;
listener->Forward[0] = 0.0f;
listener->Forward[1] = 0.0f;
listener->Forward[2] = -1.0f;
@@ -2296,9 +2300,12 @@ static ALvoid InitContext(ALCcontext *Context)
listener->Params.DopplerFactor = 1.0f;
listener->Params.SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
+ ATOMIC_INIT(&listener->Update, NULL);
+ ATOMIC_INIT(&listener->FreeList, NULL);
+
//Validate Context
+ RWLockInit(&Context->PropLock);
ATOMIC_INIT(&Context->LastError, AL_NO_ERROR);
- ATOMIC_INIT(&Context->UpdateSources, AL_FALSE);
InitUIntMap(&Context->SourceMap, Context->Device->MaxNoOfSources);
InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
@@ -2321,6 +2328,10 @@ static ALvoid InitContext(ALCcontext *Context)
*/
static void FreeContext(ALCcontext *context)
{
+ ALlistener *listener = context->Listener;
+ struct ALlistenerProps *lprops;
+ size_t count;
+
TRACE("%p\n", context);
if(context->SourceMap.size > 0)
@@ -2344,6 +2355,22 @@ static void FreeContext(ALCcontext *context)
VECTOR_DEINIT(context->ActiveAuxSlots);
+ if((lprops=ATOMIC_LOAD(&listener->Update, almemory_order_acquire)) != NULL)
+ {
+ TRACE("Freed unapplied listener update %p\n", lprops);
+ al_free(lprops);
+ }
+ count = 0;
+ lprops = ATOMIC_LOAD(&listener->FreeList, almemory_order_consume);
+ while(lprops)
+ {
+ struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_consume);
+ al_free(lprops);
+ lprops = next;
+ ++count;
+ }
+ TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
+
ALCdevice_DecRef(context->Device);
context->Device = NULL;
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 709b7127..82553cc7 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -266,19 +266,25 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
}
-static ALvoid CalcListenerParams(ALCcontext *Context)
+static ALboolean CalcListenerParams(ALCcontext *Context)
{
ALlistener *Listener = Context->Listener;
ALdouble N[3], V[3], U[3], P[3];
+ struct ALlistenerProps *first;
+ struct ALlistenerProps *props;
+ aluVector vel;
+
+ props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
+ if(!props) return AL_FALSE;
/* AT then UP */
- N[0] = Listener->Forward[0];
- N[1] = Listener->Forward[1];
- N[2] = Listener->Forward[2];
+ N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
+ N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
+ N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
aluNormalized(N);
- V[0] = Listener->Up[0];
- V[1] = Listener->Up[1];
- V[2] = Listener->Up[2];
+ V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
+ V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
+ V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
aluNormalized(V);
/* Build and normalize right-vector */
aluCrossproductd(N, V, U);
@@ -291,19 +297,37 @@ static ALvoid CalcListenerParams(ALCcontext *Context)
0.0, 0.0, 0.0, 1.0
);
- P[0] = Listener->Position.v[0];
- P[1] = Listener->Position.v[1];
- P[2] = Listener->Position.v[2];
+ P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
+ P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
+ P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
aluMatrixdDouble3(P, 1.0, &Listener->Params.Matrix);
aluMatrixdSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
- Listener->Params.Velocity = aluMatrixdVector(&Listener->Params.Matrix, &Listener->Velocity);
+ aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
+ ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
+ ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
+ 0.0f);
+ Listener->Params.Velocity = aluMatrixdVector(&Listener->Params.Matrix, &vel);
+
+ Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
+ Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
- Listener->Params.Gain = Listener->Gain;
- Listener->Params.MetersPerUnit = Listener->MetersPerUnit;
+ Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
+ Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
+ ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
- Listener->Params.DopplerFactor = Context->DopplerFactor;
- Listener->Params.SpeedOfSound = Context->SpeedOfSound * Context->DopplerVelocity;
+ /* WARNING: A livelock is theoretically possible if another thread keeps
+ * changing the freelist head without giving this a chance to actually swap
+ * in the old container (practically impossible with this little code,
+ * but...).
+ */
+ first = ATOMIC_LOAD(&Listener->FreeList);
+ do {
+ ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+ } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
+ &Listener->FreeList, &first, props) == 0);
+
+ return AL_TRUE;
}
ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
@@ -1223,10 +1247,8 @@ void UpdateContextSources(ALCcontext *ctx)
ALvoice *voice, *voice_end;
ALsource *source;
- if(ATOMIC_EXCHANGE(ALenum, &ctx->UpdateSources, AL_FALSE))
+ if(CalcListenerParams(ctx))
{
- CalcListenerParams(ctx);
-
voice = ctx->Voices;
voice_end = voice + ctx->VoiceCount;
for(;voice != voice_end;++voice)