SDL: audio: WaitDevice/WaitCaptureDevice now returns a result.

From d2d4914ac3e5233947c081ea5d68755f4ee80282 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 3 Oct 2023 10:35:18 -0400
Subject: [PATCH] audio: WaitDevice/WaitCaptureDevice now returns a result.

This is an attempt to centralize all the error handling, instead of
implicitly counting on WaitDevice implementations to disconnect the device
to report an error.
---
 src/audio/SDL_audio.c                   | 14 ++++++++++----
 src/audio/SDL_sysaudio.h                |  4 ++--
 src/audio/aaudio/SDL_aaudio.c           |  3 ++-
 src/audio/alsa/SDL_alsa_audio.c         |  8 +++++---
 src/audio/directsound/SDL_directsound.c | 16 +++++++++-------
 src/audio/disk/SDL_diskaudio.c          |  3 ++-
 src/audio/dsp/SDL_dspaudio.c            |  7 ++++---
 src/audio/dummy/SDL_dummyaudio.c        |  3 ++-
 src/audio/n3ds/SDL_n3dsaudio.c          |  3 ++-
 src/audio/netbsd/SDL_netbsdaudio.c      |  7 ++++---
 src/audio/openslES/SDL_openslES.c       |  4 ++--
 src/audio/ps2/SDL_ps2audio.c            |  3 ++-
 src/audio/psp/SDL_pspaudio.c            |  4 ++--
 src/audio/pulseaudio/SDL_pulseaudio.c   | 18 +++++++++++++-----
 src/audio/qnx/SDL_qsa_audio.c           | 16 ++++++++--------
 src/audio/sndio/SDL_sndioaudio.c        | 17 ++++++++---------
 src/audio/vita/SDL_vitaaudio.c          |  6 ++++--
 src/audio/wasapi/SDL_wasapi.c           |  6 ++++--
 18 files changed, 85 insertions(+), 57 deletions(-)

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 078e3793c53f..b9a73247bc4c 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -524,9 +524,9 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
 // stubs for audio drivers that don't need a specific entry point...
 
 static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
-static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
+static int SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { return 0; /* no-op. */ }
 static int SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return 0; /* no-op. */ }
-static void SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
+static int SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { return 0; /* no-op. */ }
 static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ }
 static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
 static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
@@ -938,7 +938,10 @@ static int SDLCALL OutputAudioThread(void *devicep)  // thread entry point
     SDL_assert(!device->iscapture);
     SDL_OutputAudioThreadSetup(device);
     do {
-        current_audio.impl.WaitDevice(device);
+        if (current_audio.impl.WaitDevice(device) < 0) {
+            SDL_AudioDeviceDisconnected(device);  // doh.
+            break;
+        }
     } while (SDL_OutputAudioThreadIterate(device));
 
     SDL_OutputAudioThreadShutdown(device);
@@ -1039,7 +1042,10 @@ static int SDLCALL CaptureAudioThread(void *devicep)  // thread entry point
     SDL_CaptureAudioThreadSetup(device);
 
     do {
-        current_audio.impl.WaitCaptureDevice(device);
+        if (current_audio.impl.WaitCaptureDevice(device) < 0) {
+            SDL_AudioDeviceDisconnected(device);  // doh.
+            break;
+        }
     } while (SDL_CaptureAudioThreadIterate(device));
 
     SDL_CaptureAudioThreadShutdown(device);
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index c49824dce1e0..284a718241ac 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -128,10 +128,10 @@ typedef struct SDL_AudioDriverImpl
     int (*OpenDevice)(SDL_AudioDevice *device);
     void (*ThreadInit)(SDL_AudioDevice *device);   // Called by audio thread at start
     void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
-    void (*WaitDevice)(SDL_AudioDevice *device);
+    int (*WaitDevice)(SDL_AudioDevice *device);
     int (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen);  // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
     Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
-    void (*WaitCaptureDevice)(SDL_AudioDevice *device);
+    int (*WaitCaptureDevice)(SDL_AudioDevice *device);
     int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
     void (*FlushCapture)(SDL_AudioDevice *device);
     void (*CloseDevice)(SDL_AudioDevice *device);
diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c
index 47fbcbb12e37..9a35ec50f21d 100644
--- a/src/audio/aaudio/SDL_aaudio.c
+++ b/src/audio/aaudio/SDL_aaudio.c
@@ -164,9 +164,10 @@ static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
     return &hidden->mixbuf[offset];
 }
 
-static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int AAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     SDL_WaitSemaphore(device->hidden->semaphore);
+    return 0;
 }
 
 static int BuildAAudioStream(SDL_AudioDevice *device);
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 74b097269a46..0826c551b6ec 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -326,7 +326,7 @@ static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
 #endif // SND_CHMAP_API_VERSION
 
 // This function waits until it is possible to write a full sound buffer
-static void ALSA_WaitDevice(SDL_AudioDevice *device)
+static int ALSA_WaitDevice(SDL_AudioDevice *device)
 {
     const int fulldelay = (int) ((((Uint64) device->sample_frames) * 1000) / device->spec.freq);
     const int delay = SDL_max(fulldelay, 10);
@@ -338,9 +338,9 @@ static void ALSA_WaitDevice(SDL_AudioDevice *device)
             if (status < 0) {
                 // Hmm, not much we can do - abort
                 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA: snd_pcm_wait failed (unrecoverable): %s", ALSA_snd_strerror(rc));
-                SDL_AudioDeviceDisconnected(device);
+                return -1;
             }
-            return;
+            continue;
         }
 
         if (rc > 0) {
@@ -349,6 +349,8 @@ static void ALSA_WaitDevice(SDL_AudioDevice *device)
 
         // Timed out! Make sure we aren't shutting down and then wait again.
     }
+
+    return 0;
 }
 
 static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c
index bf812b50b4f7..ad0d9db375bb 100644
--- a/src/audio/directsound/SDL_directsound.c
+++ b/src/audio/directsound/SDL_directsound.c
@@ -225,7 +225,7 @@ static void DSOUND_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevi
 
 }
 
-static void DSOUND_WaitDevice(SDL_AudioDevice *device)
+static int DSOUND_WaitDevice(SDL_AudioDevice *device)
 {
     /* Semi-busy wait, since we have no way of getting play notification
        on a primary mixing buffer located in hardware (DirectX 5.0)
@@ -251,12 +251,13 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *device)
         }
 
         if ((result != DS_OK) && (result != DSERR_BUFFERLOST)) {
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         }
 
         SDL_Delay(1);  // not ready yet; sleep a bit.
     }
+
+    return 0;
 }
 
 static int DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
@@ -328,19 +329,20 @@ static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
     return device->hidden->locked_buf;
 }
 
-static void DSOUND_WaitCaptureDevice(SDL_AudioDevice *device)
+static int DSOUND_WaitCaptureDevice(SDL_AudioDevice *device)
 {
     struct SDL_PrivateAudioData *h = device->hidden;
     while (!SDL_AtomicGet(&device->shutdown)) {
         DWORD junk, cursor;
         if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         } else if ((cursor / device->buffer_size) != h->lastchunk) {
-            return;
+            break;
         }
         SDL_Delay(1);
     }
+
+    return 0;
 }
 
 static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c
index be9ba9cbd7b1..695360aa3ba9 100644
--- a/src/audio/disk/SDL_diskaudio.c
+++ b/src/audio/disk/SDL_diskaudio.c
@@ -35,9 +35,10 @@
 #define DISKDEFAULT_INFILE  "sdlaudio-in.raw"
 #define DISKENVR_IODELAY    "SDL_DISKAUDIODELAY"
 
-static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     SDL_Delay(device->hidden->io_delay);
+    return 0;
 }
 
 static int DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
diff --git a/src/audio/dsp/SDL_dspaudio.c b/src/audio/dsp/SDL_dspaudio.c
index bcbf220c1eab..b75b6d9f20d5 100644
--- a/src/audio/dsp/SDL_dspaudio.c
+++ b/src/audio/dsp/SDL_dspaudio.c
@@ -201,7 +201,7 @@ static int DSP_OpenDevice(SDL_AudioDevice *device)
     return 0;  // We're ready to rock and roll. :-)
 }
 
-static void DSP_WaitDevice(SDL_AudioDevice *device)
+static int DSP_WaitDevice(SDL_AudioDevice *device)
 {
     const unsigned long ioctlreq = device->iscapture ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE;
     struct SDL_PrivateAudioData *h = device->hidden;
@@ -215,14 +215,15 @@ static void DSP_WaitDevice(SDL_AudioDevice *device)
             }
             // Hmm, not much we can do - abort
             fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         } else if (info.bytes < device->buffer_size) {
             SDL_Delay(10);
         } else {
             break; // ready to go!
         }
     }
+
+    return 0;
 }
 
 static int DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/dummy/SDL_dummyaudio.c b/src/audio/dummy/SDL_dummyaudio.c
index bd3002daa724..aa0c51ecfd85 100644
--- a/src/audio/dummy/SDL_dummyaudio.c
+++ b/src/audio/dummy/SDL_dummyaudio.c
@@ -28,9 +28,10 @@
 // !!! FIXME: this should be an SDL hint, not an environment variable.
 #define DUMMYENVR_IODELAY "SDL_DUMMYAUDIODELAY"
 
-static void DUMMYAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int DUMMYAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     SDL_Delay(device->hidden->io_delay);
+    return 0;
 }
 
 static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *device)
diff --git a/src/audio/n3ds/SDL_n3dsaudio.c b/src/audio/n3ds/SDL_n3dsaudio.c
index a7dc9fb06997..efc26542aeb8 100644
--- a/src/audio/n3ds/SDL_n3dsaudio.c
+++ b/src/audio/n3ds/SDL_n3dsaudio.c
@@ -200,7 +200,7 @@ static int N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
     return 0;
 }
 
-static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     contextLock(device);
     while (!device->hidden->isCancelled && !SDL_AtomicGet(&device->shutdown) &&
@@ -208,6 +208,7 @@ static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
         CondVar_Wait(&device->hidden->cv, &device->hidden->lock);
     }
     contextUnlock(device);
+    return 0;
 }
 
 static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
diff --git a/src/audio/netbsd/SDL_netbsdaudio.c b/src/audio/netbsd/SDL_netbsdaudio.c
index 80b1767eadde..0340bfb23f28 100644
--- a/src/audio/netbsd/SDL_netbsdaudio.c
+++ b/src/audio/netbsd/SDL_netbsdaudio.c
@@ -115,7 +115,7 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *device)
 #endif // DEBUG_AUDIO
 }
 
-static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     const SDL_bool iscapture = device->iscapture;
     while (!SDL_AtomicGet(&device->shutdown)) {
@@ -127,8 +127,7 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
             }
             // Hmm, not much we can do - abort
             fprintf(stderr, "netbsdaudio WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         }
         const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format));
         if (!iscapture && (remain >= device->buffer_size)) {
@@ -139,6 +138,8 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
             break; /* ready to go! */
         }
     }
+
+    return 0;
 }
 
 static int NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/openslES/SDL_openslES.c b/src/audio/openslES/SDL_openslES.c
index c4855eaefa4f..de041e6b4895 100644
--- a/src/audio/openslES/SDL_openslES.c
+++ b/src/audio/openslES/SDL_openslES.c
@@ -628,14 +628,14 @@ static int openslES_OpenDevice(SDL_AudioDevice *device)
     return 0;
 }
 
-static void openslES_WaitDevice(SDL_AudioDevice *device)
+static int openslES_WaitDevice(SDL_AudioDevice *device)
 {
     struct SDL_PrivateAudioData *audiodata = device->hidden;
 
     LOGV("openslES_WaitDevice()");
 
     // Wait for an audio chunk to finish
-    SDL_WaitSemaphore(audiodata->playsem);
+    return SDL_WaitSemaphore(audiodata->playsem);
 }
 
 static int openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/ps2/SDL_ps2audio.c b/src/audio/ps2/SDL_ps2audio.c
index 2165194eb182..4debff45d834 100644
--- a/src/audio/ps2/SDL_ps2audio.c
+++ b/src/audio/ps2/SDL_ps2audio.c
@@ -91,9 +91,10 @@ static int PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int
     return (audsrv_play_audio((char *)buffer, buflen) != buflen) ? -1 : 0;
 }
 
-static void PS2AUDIO_WaitDevice(SDL_AudioDevice *device)
+static int PS2AUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     audsrv_wait_audio(device->buffer_size);
+    return 0;
 }
 
 static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
diff --git a/src/audio/psp/SDL_pspaudio.c b/src/audio/psp/SDL_pspaudio.c
index d701b5f2c9fa..8e3589760062 100644
--- a/src/audio/psp/SDL_pspaudio.c
+++ b/src/audio/psp/SDL_pspaudio.c
@@ -118,9 +118,9 @@ static int PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int
     return (rc == 0) ? 0 : -1;
 }
 
-static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
-    // Because we block when sending audio, there's no need for this function to do anything.
+    return 0;  // Because we block when sending audio, there's no need for this function to do anything.
 }
 
 static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index 4d9c8cd072c3..a554fcd322d7 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -396,9 +396,10 @@ static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
 }
 
 /* This function waits until it is possible to write a full sound buffer */
-static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
+static int PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
 {
     struct SDL_PrivateAudioData *h = device->hidden;
+    int retval = 0;
 
     /*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
 
@@ -410,11 +411,14 @@ static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
 
         if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
             /*printf("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!\n");*/
-            SDL_AudioDeviceDisconnected(device);
+            retval = -1;
             break;
         }
     }
+
     PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
+
+    return retval;
 }
 
 static int PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
@@ -462,21 +466,23 @@ static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata)
     PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);  /* the capture code queries what it needs, we just need to signal to end any wait */
 }
 
-static void PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device)
+static int PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device)
 {
     struct SDL_PrivateAudioData *h = device->hidden;
 
     if (h->capturebuf != NULL) {
-        return;  // there's still data available to read.
+        return 0;  // there's still data available to read.
     }
 
+    int retval = 0;
+
     PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
 
     while (!SDL_AtomicGet(&device->shutdown)) {
         PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
         if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
             //printf("PULSEAUDIO DEVICE FAILURE IN WAITCAPTUREDEVICE!\n");
-            SDL_AudioDeviceDisconnected(device);
+            retval = -1;
             break;
         } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) {
             // a new fragment is available!
@@ -497,6 +503,8 @@ static void PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device)
     }
 
     PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
+
+    return retval;
 }
 
 static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
diff --git a/src/audio/qnx/SDL_qsa_audio.c b/src/audio/qnx/SDL_qsa_audio.c
index 7103716875c0..7c242ce94d6b 100644
--- a/src/audio/qnx/SDL_qsa_audio.c
+++ b/src/audio/qnx/SDL_qsa_audio.c
@@ -86,21 +86,19 @@ static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
 }
 
 // This function waits until it is possible to write a full sound buffer
-static void QSA_WaitDevice(SDL_AudioDevice *device)
+static int QSA_WaitDevice(SDL_AudioDevice *device)
 {
-    int result;
-
     // Setup timeout for playing one fragment equal to 2 seconds
     // If timeout occurred than something wrong with hardware or driver
     // For example, Vortex 8820 audio driver stucks on second DAC because
     // it doesn't exist !
-    result = SDL_IOReady(device->hidden->audio_fd,
-                         device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
-                         2 * 1000);
+    const int result = SDL_IOReady(device->hidden->audio_fd,
+                                   device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
+                                   2 * 1000);
     switch (result) {
     case -1:
-        SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno));  // !!! FIXME: Should we just disconnect the device in this case?
-        break;
+        SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "QSA: SDL_IOReady() failed: %s", strerror(errno));
+        return -1;
     case 0:
         device->hidden->timeout_on_wait = SDL_TRUE;  // !!! FIXME: Should we just disconnect the device in this case?
         break;
@@ -108,6 +106,8 @@ static void QSA_WaitDevice(SDL_AudioDevice *device)
         device->hidden->timeout_on_wait = SDL_FALSE;
         break;
     }
+
+    return 0;
 }
 
 static int QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/sndio/SDL_sndioaudio.c b/src/audio/sndio/SDL_sndioaudio.c
index 2b3eeb02be5a..4319d385b883 100644
--- a/src/audio/sndio/SDL_sndioaudio.c
+++ b/src/audio/sndio/SDL_sndioaudio.c
@@ -147,32 +147,31 @@ static int LoadSNDIOLibrary(void)
 
 #endif // SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
 
-static void SNDIO_WaitDevice(SDL_AudioDevice *device)
+static int SNDIO_WaitDevice(SDL_AudioDevice *device)
 {
     const SDL_bool iscapture = device->iscapture;
 
     while (!SDL_AtomicGet(&device->shutdown)) {
         if (SNDIO_sio_eof(device->hidden->dev)) {
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         }
 
         const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, iscapture ? POLLIN : POLLOUT);
         if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) {
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         }
 
         const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd);
         if (iscapture && (revents & POLLIN)) {
-            return;
+            break;
         } else if (!iscapture && (revents & POLLOUT)) {
-            return;
+            break;
         } else if (revents & POLLHUP) {
-            SDL_AudioDeviceDisconnected(device);
-            return;
+            return -1;
         }
     }
+
+    return 0;
 }
 
 static int SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
diff --git a/src/audio/vita/SDL_vitaaudio.c b/src/audio/vita/SDL_vitaaudio.c
index 53a54d104cba..7f13f0e4ead2 100644
--- a/src/audio/vita/SDL_vitaaudio.c
+++ b/src/audio/vita/SDL_vitaaudio.c
@@ -136,12 +136,13 @@ static int VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int
 }
 
 // This function waits until it is possible to write a full sound buffer
-static void VITAAUD_WaitDevice(SDL_AudioDevice *device)
+static int VITAAUD_WaitDevice(SDL_AudioDevice *device)
 {
     // !!! FIXME: we might just need to sleep roughly as long as playback buffers take to process, based on sample rate, etc.
     while (!SDL_AtomicGet(&device->shutdown) && (sceAudioOutGetRestSample(device->hidden->port) >= device->buffer_size)) {
         SDL_Delay(1);
     }
+    return 0;
 }
 
 static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@@ -172,7 +173,7 @@ static void VITAAUD_CloseDevice(SDL_AudioDevice *device)
     }
 }
 
-static void VITAAUD_WaitCaptureDevice(SDL_AudioDevice *device)
+static int VITAAUD_WaitCaptureDevice(SDL_AudioDevice *device)
 {
     // there's only a blocking call to obtain more data, so we'll just sleep as
     //  long as a buffer would run.
@@ -180,6 +181,7 @@ static void VITAAUD_WaitCaptureDevice(SDL_AudioDevice *device)
     while (!SDL_AtomicGet(&device->shutdown) && (SDL_GetTicks() < endticks)) {
         SDL_Delay(1);
     }
+    return 0;
 }
 
 static int VITAAUD_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index ab081af54dae..42aa8ef57716 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -431,7 +431,7 @@ static int WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int b
     return 0;
 }
 
-static void WASAPI_WaitDevice(SDL_AudioDevice *device)
+static int WASAPI_WaitDevice(SDL_AudioDevice *device)
 {
     // WaitDevice does not hold the device lock, so check for recovery/disconnect details here.
     while (RecoverWasapiIfLost(device) && device->hidden->client && device->hidden->event) {
@@ -450,9 +450,11 @@ static void WASAPI_WaitDevice(SDL_AudioDevice *device)
         } else if (waitResult != WAIT_TIMEOUT) {
             //SDL_Log("WASAPI FAILED EVENT!");*/
             IAudioClient_Stop(device->hidden->client);
-            WASAPI_DisconnectDevice(device);
+            return -1;
         }
     }
+
+    return 0;
 }
 
 static int WASAPI_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)