summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2010-02-20 00:18:31 +0100
committerLennart Poettering <lennart@poettering.net>2010-02-20 00:18:31 +0100
commita558bbefae4e0a9f4d2b9aa539b94ae0344f6aed (patch)
tree6f7a76deebc4a8471e305d4da33f89ad703ba8bf
parent70b59780bf23f0adc71a4cff8d87883472190ef0 (diff)
pulse: fix a minor race with sound cancellation
If we call pa_stream_drain() and immediately afterwards destroy the stream it might happen that the pa_stream_drain() callback is called after the destruction finished pointing to an invalid out structure. To fix this we need to terminate the _drain() callback to make sure the reference to the not existing structure is dropped.
-rw-r--r--src/pulse.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/src/pulse.c b/src/pulse.c
index 8dd2021..4d0d428 100644
--- a/src/pulse.c
+++ b/src/pulse.c
@@ -57,6 +57,7 @@ struct outstanding {
uint32_t id;
uint32_t sink_input;
pa_stream *stream;
+ pa_operation *drain_operation;
ca_finish_callback_t callback;
void *userdata;
ca_sound_file *file;
@@ -85,6 +86,12 @@ static void outstanding_disconnect(struct outstanding *o) {
ca_assert(o);
if (o->stream) {
+ if (o->drain_operation) {
+ pa_operation_cancel(o->drain_operation);
+ pa_operation_unref(o->drain_operation);
+ o->drain_operation = NULL;
+ }
+
pa_stream_set_write_callback(o->stream, NULL, NULL);
pa_stream_set_state_callback(o->stream, NULL, NULL);
pa_stream_disconnect(o->stream);
@@ -645,6 +652,11 @@ static void stream_drain_cb(pa_stream *s, int success, void *userdata) {
out->finished = TRUE;
}
+ if (out->drain_operation) {
+ pa_operation_unref(out->drain_operation);
+ out->drain_operation = NULL;
+ }
+
pa_threaded_mainloop_signal(p->mainloop, FALSE);
}
@@ -704,15 +716,17 @@ static void stream_write_cb(pa_stream *s, size_t bytes, void *userdata) {
pa_threaded_mainloop_signal(p->mainloop, FALSE);
} else {
- pa_operation *o;
ca_assert(out->type == OUTSTANDING_STREAM);
- if (!(o = pa_stream_drain(s, stream_drain_cb, out))) {
+ if (out->drain_operation) {
+ pa_operation_cancel(out->drain_operation);
+ pa_operation_unref(out->drain_operation);
+ }
+
+ if (!(out->drain_operation = pa_stream_drain(s, stream_drain_cb, out))) {
ret = translate_error(pa_context_errno(p->context));
goto finish;
}
-
- pa_operation_unref(o);
}
pa_stream_set_write_callback(s, NULL, NULL);