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.
This commit is contained in:
Wim Taymans 2019-03-14 13:23:20 +01:00
parent cb7b25277b
commit 49c99f8dee
3 changed files with 65 additions and 4 deletions

View file

@ -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))

View file

@ -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);

View file

@ -23,6 +23,7 @@
#include <pipewire/loop.h>
#include <pulse/mainloop.h>
#include <pulse/xmalloc.h>
#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);
}