Mypal/media/libcubeb/bug1302231_emergency_bailou...

141 lines
4.2 KiB
Diff

From 37ce70d4400a2ab6b59ee432b41d4ffcc9d136ff Mon Sep 17 00:00:00 2001
From: Paul Adenot <paul@paul.cx>
Date: Thu, 10 Nov 2016 21:45:14 +0100
Subject: [PATCH] Bail out safely from the rendering loop if we could not join
the rendering thread in time (#187)
Bail out safely from the rendering loop if we could not join the rendering thread in time.
---
src/cubeb_wasapi.cpp | 41 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp
index 9e689b9..519d5ca 100644
--- a/src/cubeb_wasapi.cpp
+++ b/src/cubeb_wasapi.cpp
@@ -22,6 +22,7 @@
#include <algorithm>
#include <memory>
#include <limits>
+#include <atomic>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
@@ -220,9 +221,11 @@ struct cubeb_stream
float volume;
/* True if the stream is draining. */
bool draining;
+ /* True when we've destroyed the stream. This pointer is leaked on stream
+ * destruction if we could not join the thread. */
+ std::atomic<std::atomic<bool>*> emergency_bailout;
};
-
class wasapi_endpoint_notification_client : public IMMNotificationClient
{
public:
@@ -781,6 +784,7 @@ static unsigned int __stdcall
wasapi_stream_render_loop(LPVOID stream)
{
cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
+ std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
bool is_playing = true;
HANDLE wait_array[4] = {
@@ -820,6 +824,10 @@ wasapi_stream_render_loop(LPVOID stream)
wait_array,
FALSE,
1000);
+ if (*emergency_bailout) {
+ delete emergency_bailout;
+ return 0;
+ }
if (waitResult != WAIT_TIMEOUT) {
timeout_count = 0;
}
@@ -1134,12 +1142,13 @@ int wasapi_init(cubeb ** context, char const * context_name)
}
namespace {
-void stop_and_join_render_thread(cubeb_stream * stm)
+bool stop_and_join_render_thread(cubeb_stream * stm)
{
+ bool rv = true;
LOG("Stop and join render thread.");
if (!stm->thread) {
LOG("No thread present.");
- return;
+ return true;
}
BOOL ok = SetEvent(stm->shutdown_event);
@@ -1153,11 +1162,15 @@ void stop_and_join_render_thread(cubeb_stream * stm)
if (r == WAIT_TIMEOUT) {
/* Something weird happened, leak the thread and continue the shutdown
* process. */
+ *(stm->emergency_bailout) = true;
LOG("Destroy WaitForSingleObject on thread timed out,"
" leaking the thread: %d", GetLastError());
+ rv = false;
}
if (r == WAIT_FAILED) {
+ *(stm->emergency_bailout) = true;
LOG("Destroy WaitForSingleObject on thread failed: %d", GetLastError());
+ rv = false;
}
LOG("Closing thread.");
@@ -1167,6 +1180,8 @@ void stop_and_join_render_thread(cubeb_stream * stm)
CloseHandle(stm->shutdown_event);
stm->shutdown_event = 0;
+
+ return rv;
}
void wasapi_destroy(cubeb * context)
@@ -1775,7 +1790,16 @@ void wasapi_stream_destroy(cubeb_stream * stm)
{
XASSERT(stm);
- stop_and_join_render_thread(stm);
+ // Only free stm->emergency_bailout if we could not join the thread.
+ // If we could not join the thread, stm->emergency_bailout is true
+ // and is still alive until the thread wakes up and exits cleanly.
+ if (stop_and_join_render_thread(stm)) {
+ delete stm->emergency_bailout.load();
+ stm->emergency_bailout = nullptr;
+ } else {
+ // If we're leaking, it must be that this is true.
+ assert(*(stm->emergency_bailout));
+ }
unregister_notification_client(stm);
@@ -1844,6 +1868,8 @@ int wasapi_stream_start(cubeb_stream * stm)
auto_lock lock(stm->stream_reset_lock);
+ stm->emergency_bailout = new std::atomic<bool>(false);
+
if (stm->output_client) {
int rv = stream_start_one_side(stm, OUTPUT);
if (rv != CUBEB_OK) {
@@ -1903,7 +1929,12 @@ int wasapi_stream_stop(cubeb_stream * stm)
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
}
- stop_and_join_render_thread(stm);
+ if (stop_and_join_render_thread(stm)) {
+ if (stm->emergency_bailout.load()) {
+ delete stm->emergency_bailout.load();
+ stm->emergency_bailout = nullptr;
+ }
+ }
return CUBEB_OK;
}
--
2.7.4