[alsa-devel] Races in alsa-lib with threads
We've found a race with alsa-lib functions are called from multiple threads. I was under the impression that alsa-lib was supposed to be thread safe. Is this not the case and all alsa calls should be done one from thread or protected by a mutex?
The race we found was in sync_ptr1() in pcm_hw.c. This issues the ioctl() to get the current hw ptr from the kernel and send the app ptr to the kernel. The communication is done by passing a pointer to hw->sync_ptr, a field in the snd_pcm_hw_t structure for the pcm. Any thread calling sync_ptr1 will pass the same structure to the kernel. This should be a red flag for a race, having two threads write to the same data structure at the same time.
This function gets called by snd_pcm_writei(), as part of that function getting the current hw pointer so it can calculate the avail value. And again when it actually writes data to commit the new app pointer to the kernel.
It also gets called by snd_pcm_delay() if a rate plugin is used. snd_pcm_rate_delay() will end up calling snd_pcm_hw_hwsync() and that does a sync_ptr1() call.
So imaging two threads, one writing data and calling snd_pcm_writei() and another calling snd_pcm_delay() as part of trying to sync audio playback. gstreamer does this!
The ioctl is handled by snd_pcm_sync_ptr() in pcm_native.c. It gets the stream lock while it reads/writes the various fields a sync struct on the stack, then releases the lock and calls copy_to_user to write the sync struct from its stack to userspace.
Here's how the race works. Thread 1 calls snd_pcm_delay(). sync_ptr1 is called snd_pcm_sync_ptr gets the lock, fills a struct with the current data, releases the lock. Does not call copy_to_user yet! Thread 2 runs! It calls snd_pcm_writei(), actually writes data too It calls sync_ptr1() to update the app pointer with the new data and returns The snd_pcm_writei() call returns Thread 1 runs again, calls copy_to_user The copy_to_user writes the OLD struct that was prepared back on the 3rd line into userspace The hw and app pointer values, as far as the userspace copy in alsa-lib is concerned, have regressed to what they were before the call to snd_pcm_writei()!
You get an audio stutter as the data written is overwritten by the next write. The race turns out to not be that unlikely. The stream lock is held and then released before calling copy_to_user. When it's released irqs are enabled. If a period has elapsed while the lock was held, the irq handle runs now, the elapsing period will wake anything blocked in poll() waiting for space, which then probably gets to run immediately since the schedule likes tasks that have been sleeping and just woke, and the new task was probably blocked waiting to write data so that's what it does as soon as it runs.
At Fri, 9 Nov 2012 18:03:25 -0800, Trent Piepho wrote:
We've found a race with alsa-lib functions are called from multiple threads. I was under the impression that alsa-lib was supposed to be thread safe. Is this not the case and all alsa calls should be done one from thread or protected by a mutex?
Yes. alsa-lib isn't thread-safe.
Takashi
Date 10.11.2012 14:53, Takashi Iwai wrote:
At Fri, 9 Nov 2012 18:03:25 -0800, Trent Piepho wrote:
We've found a race with alsa-lib functions are called from multiple threads. I was under the impression that alsa-lib was supposed to be thread safe. Is this not the case and all alsa calls should be done one from thread or protected by a mutex?
Yes. alsa-lib isn't thread-safe.
The alsa-lib is designed to be thread safe but the calls for one handle (PCM, control, rawmidi etc.) should be serialized using mutexes in apps. Or basically, it's assumed that one thread will maintain one handle.
This should be documented somewhere.
Jaroslav
Jaroslav Kysela <perex <at> perex.cz> writes:
Date 10.11.2012 14:53, Takashi Iwai wrote:
At Fri, 9 Nov 2012 18:03:25 -0800, Trent Piepho wrote:
We've found a race with alsa-lib functions are called from multiple threads. I was under the impression that alsa-lib was supposed to be thread safe. Is this not the case and all alsa calls should be done one from thread or protected by a mutex?
Yes. alsa-lib isn't thread-safe.
The alsa-lib is designed to be thread safe but the calls for one handle (PCM, control, rawmidi etc.) should be serialized using mutexes in apps. Or basically, it's assumed that one thread will maintain one handle.
This should be documented somewhere.
Jaroslav
Hi Jaroslav,
Would you be able to point me the the ALSA documentation that indicates the stipulations on handle usage using multiple threads? I cannot find it.
Best Regards,
Rob Krakora
At Mon, 12 Nov 2012 19:40:42 +0000 (UTC), Rob Krakora wrote:
Jaroslav Kysela <perex <at> perex.cz> writes:
Date 10.11.2012 14:53, Takashi Iwai wrote:
At Fri, 9 Nov 2012 18:03:25 -0800, Trent Piepho wrote:
We've found a race with alsa-lib functions are called from multiple threads. I was under the impression that alsa-lib was supposed to be thread safe. Is this not the case and all alsa calls should be done one from thread or protected by a mutex?
Yes. alsa-lib isn't thread-safe.
The alsa-lib is designed to be thread safe but the calls for one handle (PCM, control, rawmidi etc.) should be serialized using mutexes in apps. Or basically, it's assumed that one thread will maintain one handle.
This should be documented somewhere.
Jaroslav
Hi Jaroslav,
Would you be able to point me the the ALSA documentation that indicates the stipulations on handle usage using multiple threads? I cannot find it.
Think other way round: The fact that it isn't documented means it's not safe to use in that way :)
Takashi
On Tue, Nov 13, 2012 at 1:32 AM, Takashi Iwai tiwai@suse.de wrote:
At Mon, 12 Nov 2012 19:40:42 +0000 (UTC), Rob Krakora wrote:
Would you be able to point me the the ALSA documentation that indicates the stipulations on handle usage using multiple threads? I cannot find it.
Think other way round: The fact that it isn't documented means it's not safe to use in that way :)
The introduction on the alsa-project home page says, "SMP and thread-safe design. " Some people might misunderstand what thread-safe means. Maybe some clarification could be added. "Different streams are SMP and thread-safe (calls for the same stream must be serialized)."
At Tue, 13 Nov 2012 02:14:08 -0500, Trent Piepho wrote:
On Tue, Nov 13, 2012 at 1:32 AM, Takashi Iwai tiwai@suse.de wrote:
At Mon, 12 Nov 2012 19:40:42 +0000 (UTC), Rob Krakora wrote:
Would you be able to point me the the ALSA documentation that indicates the stipulations on handle usage using multiple threads? I cannot find it.
Think other way round: The fact that it isn't documented means it's not safe to use in that way :)
The introduction on the alsa-project home page says, "SMP and thread-safe design. " Some people might misunderstand what thread-safe means. Maybe some clarification could be added. "Different streams are SMP and thread-safe (calls for the same stream must be serialized)."
Yeah this should be corrected. SMP things are just about the kernel side at that time.
It's a Wiki, feel free to correct / improve the text.
Takashi
participants (4)
-
Jaroslav Kysela
-
Rob Krakora
-
Takashi Iwai
-
Trent Piepho