Lock-free dmix considered harmful

Maarten Baert maarten-baert at hotmail.com
Tue May 19 18:41:58 CEST 2020


On 19/05/2020 15:46, Takashi Iwai wrote:
> Hm, I'm not sure whether whether this really leads to the deadlock,
> but I can't exclude such a possibility in some corner cases.

It is not a deadlock, rather there exist scenarios where the buffer is 
cleared more than once, and since clearing is done by subtracting the 
old value, this results in incorrect audio.

Consider the scenario where before mixing starts, *dst = 0 and *sum = 
100. Now consider that application A plays back 10 and application B 
plays back 0. The expected result is 10. However this can happen:

A: sample = *src;                                 (A:sample == 10)
A: old_sample = *sum;                             (A:old_sample == 100)

<< here A gets interrupted by B>

B: sample = *src;                                 (B:sample == 0)
B: old_sample = *sum;                             (B:old_sample == 100)
B: if (ARCH_CMPXCHG(dst, 0, 1) == 0)              (*dst == 1)
B:     sample -= old_sample;                      (B:sample == -100)
B: ARCH_ADD(sum, sample);                         (*sum == 0)
B: [copy sum to dst]                              (*dst == 0)

<< here A resumes >>

A: if (ARCH_CMPXCHG(dst, 0, 1) == 0)              (*dst == 1)
A:     sample -= old_sample;                      (A:sample == -90)
A: ARCH_ADD(sum, sample);                         (*sum == -90)
A: [copy sum to dst]                              (*dst == -90)

In the end, *sum and *dst are -90 instead of 10.
> The advantage of lockless dmix implementation isn't about its CPU usage
> but the nature where a stream isn't prevented by other streams, which
> assures the very low latency, too.  That is, with the generic dmix, a
> stream can be still halted when another stream stalls in the middle,
> and there is no way to recover from it.

I agree, however considering that the lock has (unintentionally) been 
enabled for 13 years, and no one noticed until now, it doesn't seem to 
be a problem. I think there is a higher risk that removing the lock will 
expose new bugs due to flaws in the lock-free algorithm that were 
previously hidden by the lock.

> So, IMO, we can start with a dynamic configuration to choose the
> behavior and add a configure option to choose the implementations.
> The default behavior should be still an open question, though.

I can do that too, but monitoring is significantly easier to implement 
in the locked case. Also, I see no way to fix the lock-free algorithm 
without making changes to the shared memory layout (which would break 
compatibility between ALSA versions, which is a problem for anyone who 
uses chroots or for some reason has multiple versions of libasound on 
their system (e.g. Steam includes its own version of libasound). And I 
would rather not implement a lock-free implementation which I know is 
not fully correct.

Best regards,
Maarten Baert



More information about the Alsa-devel mailing list