SDL: wasapi: ResetWasapiDevice no longer blocks on management thread.

From 89408a97056722fe8f0a836ea6809793c86212f1 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 9 Nov 2023 20:27:58 -0500
Subject: [PATCH] wasapi: ResetWasapiDevice no longer blocks on management
 thread.

It just proxies all its necessary releases and frees to these without
blocking, and sets the appropriate fields to NULL so they can be used again
immediately, regardless of when the old stuff actually gets released.
---
 src/audio/wasapi/SDL_wasapi.c | 64 +++++++++++++++++++++++------------
 1 file changed, 42 insertions(+), 22 deletions(-)

diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index c7d251d6eb82..934ac4e6e6c2 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -294,6 +294,14 @@ static SDL_bool WasapiFailed(SDL_AudioDevice *device, const HRESULT err)
     return SDL_TRUE;
 }
 
+static int mgmtthrtask_StopAndReleaseClient(void *userdata)
+{
+    IAudioClient *client = (IAudioClient *) userdata;
+    IAudioClient_Stop(client);
+    IAudioClient_Release(client);
+    return 0;
+}
+
 static int mgmtthrtask_ReleaseCaptureClient(void *userdata)
 {
     IAudioCaptureClient_Release((IAudioCaptureClient *)userdata);
@@ -306,56 +314,68 @@ static int mgmtthrtask_ReleaseRenderClient(void *userdata)
     return 0;
 }
 
-static int mgmtthrtask_ResetWasapiDevice(void *userdata)
+static int mgmtthrtask_CoTaskMemFree(void *userdata)
 {
-    SDL_AudioDevice *device = (SDL_AudioDevice *)userdata;
+    CoTaskMemFree(userdata);
+    return 0;
+}
 
+static int mgmtthrtask_PlatformDeleteActivationHandler(void *userdata)
+{
+    WASAPI_PlatformDeleteActivationHandler(userdata);
+    return 0;
+}
+
+static int mgmtthrtask_CloseHandle(void *userdata)
+{
+    CloseHandle((HANDLE) userdata);
+    return 0;
+}
+
+static void ResetWasapiDevice(SDL_AudioDevice *device)
+{
     if (!device || !device->hidden) {
-        return 0;
+        return;
     }
 
+    // just queue up all the tasks in the management thread and don't block.
+    // We don't care when any of these actually get free'd.
+
     if (device->hidden->client) {
-        IAudioClient_Stop(device->hidden->client);
-        IAudioClient_Release(device->hidden->client);
+        IAudioClient *client = device->hidden->client;
         device->hidden->client = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_StopAndReleaseClient, client, NULL);
     }
 
     if (device->hidden->render) {
-        // this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
-        //  proxy this to the management thread to be released later.
-        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseRenderClient, device->hidden->render, NULL);
+        IAudioRenderClient *render = device->hidden->render;
         device->hidden->render = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseRenderClient, render, NULL);
     }
 
     if (device->hidden->capture) {
-        // this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
-        //  proxy this to the management thread to be released later.
-        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseCaptureClient, device->hidden->capture, NULL);
+        IAudioCaptureClient *capture = device->hidden->capture;
         device->hidden->capture = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseCaptureClient, capture, NULL);
     }
 
     if (device->hidden->waveformat) {
-        CoTaskMemFree(device->hidden->waveformat);
+        void *ptr = device->hidden->waveformat;
         device->hidden->waveformat = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_CoTaskMemFree, ptr, NULL);
     }
 
     if (device->hidden->activation_handler) {
-        WASAPI_PlatformDeleteActivationHandler(device->hidden->activation_handler);
+        void *activation_handler = device->hidden->activation_handler;
         device->hidden->activation_handler = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_PlatformDeleteActivationHandler, activation_handler, NULL);
     }
 
     if (device->hidden->event) {
-        CloseHandle(device->hidden->event);
+        HANDLE event = device->hidden->event;
         device->hidden->event = NULL;
+        WASAPI_ProxyToManagementThread(mgmtthrtask_CloseHandle, (void *) event, NULL);
     }
-
-    return 0;
-}
-
-static void ResetWasapiDevice(SDL_AudioDevice *device)
-{
-    int rc;
-    WASAPI_ProxyToManagementThread(mgmtthrtask_ResetWasapiDevice, device, &rc);
 }
 
 static int mgmtthrtask_ActivateDevice(void *userdata)