1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
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
|