summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeungha Yang <seungha@centricular.com>2020-06-09 22:38:28 +0900
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>2020-09-28 19:08:54 +0000
commit432bc35d5737492000ea60470400ff176071aabb (patch)
treeb82e65cb83d8bba80743f3c0d0e0b833a9041dff
parent235e7d2ff96c43c80cbf99607a706940d9b49382 (diff)
downloadgstreamer-plugins-bad-432bc35d5737492000ea60470400ff176071aabb.tar.gz
wasapi: Fix possible deadlock while downwards state change
IAudioClient::Stop() doesn't seem to wake up the event handle, then read() or write() could be blocked forever by WaitForSingleObject. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1610>
-rw-r--r--sys/wasapi/gstwasapisink.c39
-rw-r--r--sys/wasapi/gstwasapisink.h1
-rw-r--r--sys/wasapi/gstwasapisrc.c28
-rw-r--r--sys/wasapi/gstwasapisrc.h1
4 files changed, 62 insertions, 7 deletions
diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c
index f7c4fe68f..573925144 100644
--- a/sys/wasapi/gstwasapisink.c
+++ b/sys/wasapi/gstwasapisink.c
@@ -175,6 +175,7 @@ gst_wasapi_sink_init (GstWasapiSink * self)
self->low_latency = DEFAULT_LOW_LATENCY;
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
+ self->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
self->client_needs_restart = FALSE;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
@@ -190,6 +191,11 @@ gst_wasapi_sink_dispose (GObject * object)
self->event_handle = NULL;
}
+ if (self->cancellable != NULL) {
+ CloseHandle (self->cancellable);
+ self->cancellable = NULL;
+ }
+
if (self->client != NULL) {
IUnknown_Release (self->client);
self->client = NULL;
@@ -564,6 +570,9 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
res = TRUE;
+ /* reset cancellable event handle */
+ ResetEvent (self->cancellable);
+
beach:
/* unprepare() is not called if prepare() fails, but we want it to be, so call
* it manually when needed */
@@ -600,6 +609,10 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
gint16 *dst = NULL;
DWORD dwWaitResult;
guint can_frames, have_frames, n_frames, write_len, written_len = 0;
+ HANDLE event_handle[2];
+
+ event_handle[0] = self->event_handle;
+ event_handle[1] = self->cancellable;
GST_OBJECT_LOCK (self);
if (self->client_needs_restart) {
@@ -607,6 +620,7 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
GST_OBJECT_UNLOCK (self); goto err);
self->client_needs_restart = FALSE;
+ ResetEvent (self->cancellable);
}
GST_OBJECT_UNLOCK (self);
@@ -614,14 +628,20 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
have_frames = length / (self->mix_format->nBlockAlign);
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
- /* In exlusive mode we have to wait always */
- dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
- if (dwWaitResult != WAIT_OBJECT_0) {
+ /* In exclusive mode we have to wait always */
+ dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
+ if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
(guint) dwWaitResult);
goto err;
}
+ /* ::reset was requested */
+ if (dwWaitResult == WAIT_OBJECT_0 + 1) {
+ GST_DEBUG_OBJECT (self, "operation was cancelled");
+ return -1;
+ }
+
can_frames = gst_wasapi_sink_get_can_frames (self);
if (can_frames < 0) {
GST_ERROR_OBJECT (self, "Error getting frames to write to");
@@ -645,12 +665,19 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
}
if (can_frames == 0) {
- dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
- if (dwWaitResult != WAIT_OBJECT_0) {
+ dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
+ if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
(guint) dwWaitResult);
goto err;
}
+
+ /* ::reset was requested */
+ if (dwWaitResult == WAIT_OBJECT_0 + 1) {
+ GST_DEBUG_OBJECT (self, "operation was cancelled");
+ return -1;
+ }
+
can_frames = gst_wasapi_sink_get_can_frames (self);
if (can_frames < 0) {
GST_ERROR_OBJECT (self, "Error getting frames to write to");
@@ -713,6 +740,8 @@ gst_wasapi_sink_reset (GstAudioSink * asink)
if (!self->client)
return;
+ SetEvent (self->cancellable);
+
GST_OBJECT_LOCK (self);
hr = IAudioClient_Stop (self->client);
HR_FAILED_AND (hr, IAudioClient::Stop, goto err);
diff --git a/sys/wasapi/gstwasapisink.h b/sys/wasapi/gstwasapisink.h
index 7806fc301..76ec38fb1 100644
--- a/sys/wasapi/gstwasapisink.h
+++ b/sys/wasapi/gstwasapisink.h
@@ -44,6 +44,7 @@ struct _GstWasapiSink
IAudioClient *client;
IAudioRenderClient *render_client;
HANDLE event_handle;
+ HANDLE cancellable;
/* Client was reset, so it needs to be started again */
gboolean client_needs_restart;
diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c
index 4f63edce0..deb22e70b 100644
--- a/sys/wasapi/gstwasapisrc.c
+++ b/sys/wasapi/gstwasapisrc.c
@@ -189,6 +189,7 @@ gst_wasapi_src_init (GstWasapiSrc * self)
self->low_latency = DEFAULT_LOW_LATENCY;
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
+ self->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
self->client_needs_restart = FALSE;
self->adapter = gst_adapter_new ();
@@ -205,6 +206,11 @@ gst_wasapi_src_dispose (GObject * object)
self->event_handle = NULL;
}
+ if (self->cancellable != NULL) {
+ CloseHandle (self->cancellable);
+ self->cancellable = NULL;
+ }
+
if (self->client_clock != NULL) {
IUnknown_Release (self->client_clock);
self->client_clock = NULL;
@@ -518,7 +524,12 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
(self)->ringbuffer, self->positions);
res = TRUE;
+
+ /* reset cancellable event handle */
+ ResetEvent (self->cancellable);
+
beach:
+
/* unprepare() is not called if prepare() fails, but we want it to be, so call
* it manually when needed */
if (!res)
@@ -570,6 +581,7 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
GST_OBJECT_UNLOCK (self); goto err);
self->client_needs_restart = FALSE;
+ ResetEvent (self->cancellable);
gst_adapter_clear (self->adapter);
}
@@ -587,15 +599,25 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
while (wanted > 0) {
DWORD dwWaitResult;
guint got_frames, avail_frames, n_frames, want_frames, read_len;
+ HANDLE event_handle[2];
+
+ event_handle[0] = self->event_handle;
+ event_handle[1] = self->cancellable;
/* Wait for data to become available */
- dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
- if (dwWaitResult != WAIT_OBJECT_0) {
+ dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
+ if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
(guint) dwWaitResult);
goto err;
}
+ /* ::reset was requested */
+ if (dwWaitResult == WAIT_OBJECT_0 + 1) {
+ GST_DEBUG_OBJECT (self, "operation was cancelled");
+ return -1;
+ }
+
hr = IAudioCaptureClient_GetBuffer (self->capture_client,
(BYTE **) & from, &got_frames, &flags, NULL, NULL);
if (hr != S_OK) {
@@ -687,6 +709,8 @@ gst_wasapi_src_reset (GstAudioSrc * asrc)
if (!self->client)
return;
+ SetEvent (self->cancellable);
+
GST_OBJECT_LOCK (self);
hr = IAudioClient_Stop (self->client);
HR_FAILED_AND (hr, IAudioClock::Stop, goto err);
diff --git a/sys/wasapi/gstwasapisrc.h b/sys/wasapi/gstwasapisrc.h
index 021ef464b..3e2c968d8 100644
--- a/sys/wasapi/gstwasapisrc.h
+++ b/sys/wasapi/gstwasapisrc.h
@@ -46,6 +46,7 @@ struct _GstWasapiSrc
guint64 client_clock_freq;
IAudioCaptureClient *capture_client;
HANDLE event_handle;
+ HANDLE cancellable;
/* Smooth frames captured from WASAPI, which can be irregular sometimes */
GstAdapter *adapter;
/* Client was reset, so it needs to be started again */