From 49c99f8dee4b42f3c53fe8bc20e977f6e6b3cb94 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 14 Mar 2019 13:23:20 +0100 Subject: [PATCH] context: handle context errors When the context is in error it might be unreffed. Make sure we don't ever unref the core from the callback because that is not allowed. Instead, add a defered to the mainloop to clean up later. --- src/context.c | 15 ++++++++++++++- src/internal.h | 7 ++++--- src/mainloop.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index b6f18da6f..8c674b9b8 100644 --- a/src/context.c +++ b/src/context.c @@ -63,6 +63,10 @@ static void context_unlink(pa_context *c) pw_log_debug("context %p: unlink %d", c, c->state); + c->disconnect = true; + c->state_callback = NULL; + c->state_userdata = NULL; + spa_list_for_each_safe(s, t, &c->streams, link) { pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); @@ -397,6 +401,8 @@ static void remote_state_changed(void *data, enum pw_remote_state old, context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; case PW_REMOTE_STATE_UNCONNECTED: + if (!c->disconnect) + context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; case PW_REMOTE_STATE_CONNECTING: pa_context_set_state(c, PA_CONTEXT_CONNECTING); @@ -480,6 +486,12 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * return c; } +static void do_core_destroy(pa_mainloop_api*m, void *userdata) +{ + pa_context *c = userdata; + pw_core_destroy(c->core); +} + static void context_free(pa_context *c) { pw_log_debug("context %p: free", c); @@ -491,7 +503,7 @@ static void context_free(pa_context *c) if (c->core_info) pw_core_info_free(c->core_info); - pw_core_destroy(c->core); + pa_mainloop_api_once(c->mainloop, do_core_destroy, c); } SPA_EXPORT @@ -598,6 +610,7 @@ void pa_context_disconnect(pa_context *c) pa_assert(c); pa_assert(c->refcount >= 1); + c->disconnect = true; pw_remote_disconnect(c->remote); if (PA_CONTEXT_IS_GOOD(c->state)) diff --git a/src/internal.h b/src/internal.h index c562531f2..bc9e30cca 100644 --- a/src/internal.h +++ b/src/internal.h @@ -257,6 +257,7 @@ struct global { struct pa_context { int refcount; + uint32_t client_index; struct pw_loop *loop; struct pw_core *core; @@ -284,13 +285,13 @@ struct pa_context { void *subscribe_userdata; pa_subscription_mask_t subscribe_mask; - bool no_fail; - uint32_t client_index; - struct spa_list globals; struct spa_list streams; struct spa_list operations; + + int no_fail:1; + int disconnect:1; }; struct global *pa_context_find_global(pa_context *c, uint32_t id); diff --git a/src/mainloop.c b/src/mainloop.c index ef509ece1..e5c85589c 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -23,6 +23,7 @@ #include #include +#include #include "internal.h" @@ -376,3 +377,49 @@ void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *use { pw_log_warn("Not Implemented"); } + + +struct once_info { + void (*callback)(pa_mainloop_api*m, void *userdata); + void *userdata; +}; + +static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + + pa_assert(m); + pa_assert(i); + + pa_assert(i->callback); + i->callback(m, i->userdata); + + pa_assert(m->defer_free); + m->defer_free(e); +} + +static void free_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + + pa_assert(m); + pa_assert(i); + pa_xfree(i); +} + + +void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { + struct once_info *i; + pa_defer_event *e; + + pa_assert(m); + pa_assert(callback); + + pa_init_i18n(); + + i = pa_xnew(struct once_info, 1); + i->callback = callback; + i->userdata = userdata; + + pa_assert(m->defer_new); + pa_assert_se(e = m->defer_new(m, once_callback, i)); + m->defer_set_destroy(e, free_callback); +}