alsa: Make hotplug thread optional.

Even without the thread, it'll do an initial hardware detection at startup,
but there won't be any further hotplug events after that. But for many cases,
that is likely complete sufficient.

In either case, this cleaned up the code to no longer need a semaphore at
startup.

Fixes #4862.
This commit is contained in:
Ryan C. Gordon 2021-10-30 16:02:12 -04:00
parent 26706319d7
commit 8a4a282aaa
No known key found for this signature in database
GPG key ID: FA148B892AB48044

View file

@ -26,6 +26,11 @@
#define SDL_ALSA_NON_BLOCKING 0 #define SDL_ALSA_NON_BLOCKING 0
#endif #endif
/* without the thread, you will detect devices on startup, but will not get futher hotplug events. But that might be okay. */
#ifndef SDL_ALSA_HOTPLUG_THREAD
#define SDL_ALSA_HOTPLUG_THREAD 1
#endif
/* Allow access to a raw mixing buffer */ /* Allow access to a raw mixing buffer */
#include <sys/types.h> #include <sys/types.h>
@ -802,24 +807,16 @@ add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSee
} }
static SDL_atomic_t ALSA_hotplug_shutdown; static ALSA_Device *hotplug_devices = NULL;
static SDL_Thread *ALSA_hotplug_thread;
static int SDLCALL static void
ALSA_HotplugThread(void *arg) ALSA_HotplugIteration(void)
{ {
SDL_sem *first_run_semaphore = (SDL_sem *) arg;
ALSA_Device *devices = NULL;
ALSA_Device *next;
ALSA_Device *dev;
Uint32 ticks;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
void **hints = NULL; void **hints = NULL;
ALSA_Device *dev;
ALSA_Device *unseen; ALSA_Device *unseen;
ALSA_Device *seen; ALSA_Device *seen;
ALSA_Device *next;
ALSA_Device *prev; ALSA_Device *prev;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
@ -832,8 +829,9 @@ ALSA_HotplugThread(void *arg)
"hw:", "sysdefault:", "default:", NULL "hw:", "sysdefault:", "default:", NULL
}; };
unseen = devices; unseen = hotplug_devices;
seen = NULL; seen = NULL;
/* Apparently there are several different ways that ALSA lists /* Apparently there are several different ways that ALSA lists
actual hardware. It could be prefixed with "hw:" or "default:" actual hardware. It could be prefixed with "hw:" or "default:"
or "sysdefault:" and maybe others. Go through the list and see or "sysdefault:" and maybe others. Go through the list and see
@ -924,7 +922,7 @@ ALSA_HotplugThread(void *arg)
ALSA_snd_device_name_free_hint(hints); ALSA_snd_device_name_free_hint(hints);
devices = seen; /* now we have a known-good list of attached devices. */ hotplug_devices = seen; /* now we have a known-good list of attached devices. */
/* report anything still in unseen as removed. */ /* report anything still in unseen as removed. */
for (dev = unseen; dev; dev = next) { for (dev = unseen; dev; dev = next) {
@ -935,58 +933,64 @@ ALSA_HotplugThread(void *arg)
SDL_free(dev); SDL_free(dev);
} }
} }
}
/* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */ #if SDL_ALSA_HOTPLUG_THREAD
if (first_run_semaphore) { static SDL_atomic_t ALSA_hotplug_shutdown;
SDL_SemPost(first_run_semaphore); static SDL_Thread *ALSA_hotplug_thread;
first_run_semaphore = NULL; /* let other thread clean it up. */
}
static int SDLCALL
ALSA_HotplugThread(void *arg)
{
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
/* Block awhile before checking again, unless we're told to stop. */ /* Block awhile before checking again, unless we're told to stop. */
ticks = SDL_GetTicks() + 5000; const Uint32 ticks = SDL_GetTicks() + 5000;
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) { while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
SDL_Delay(100); SDL_Delay(100);
} }
}
/* Shutting down! Clean up any data we've gathered. */ ALSA_HotplugIteration(); /* run the check. */
for (dev = devices; dev; dev = next) {
/*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
next = dev->next;
SDL_free(dev->name);
SDL_free(dev);
} }
return 0; return 0;
} }
#endif
static void static void
ALSA_DetectDevices(void) ALSA_DetectDevices(void)
{ {
/* Start the device detection thread here, wait for an initial iteration to complete. */ ALSA_HotplugIteration(); /* run once now before a thread continues to check. */
SDL_sem *semaphore = SDL_CreateSemaphore(0);
if (!semaphore) {
return; /* oh well. */
}
#if SDL_ALSA_HOTPLUG_THREAD
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0); SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL);
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore); /* if the thread doesn't spin, oh well, you just don't get further hotplug events. */
if (ALSA_hotplug_thread) { #endif
SDL_SemWait(semaphore); /* wait for the first iteration to finish. */
}
SDL_DestroySemaphore(semaphore);
} }
static void static void
ALSA_Deinitialize(void) ALSA_Deinitialize(void)
{ {
ALSA_Device *dev;
ALSA_Device *next;
#if SDL_ALSA_HOTPLUG_THREAD
if (ALSA_hotplug_thread != NULL) { if (ALSA_hotplug_thread != NULL) {
SDL_AtomicSet(&ALSA_hotplug_shutdown, 1); SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
SDL_WaitThread(ALSA_hotplug_thread, NULL); SDL_WaitThread(ALSA_hotplug_thread, NULL);
ALSA_hotplug_thread = NULL; ALSA_hotplug_thread = NULL;
} }
#endif
/* Shutting down! Clean up any data we've gathered. */
for (dev = hotplug_devices; dev; dev = next) {
/*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
next = dev->next;
SDL_free(dev->name);
SDL_free(dev);
}
UnloadALSALibrary(); UnloadALSALibrary();
} }