mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
jack: fix a potential deadlock
When some blocking method is performed on the process thread, like jack_connect() mark the data thread as blocked while we wait for the thread loop to process the result. If we then try to do a blocking _invoke from the thread loop on the data thread, make sure we don't wait for it to complete or else we deadlock.
This commit is contained in:
parent
5137015c33
commit
838ab996d0
1 changed files with 26 additions and 20 deletions
|
|
@ -380,6 +380,7 @@ struct client {
|
||||||
|
|
||||||
pthread_mutex_t rt_lock;
|
pthread_mutex_t rt_lock;
|
||||||
unsigned int rt_locked:1;
|
unsigned int rt_locked:1;
|
||||||
|
unsigned int data_locked:1;
|
||||||
|
|
||||||
unsigned int started:1;
|
unsigned int started:1;
|
||||||
unsigned int active:1;
|
unsigned int active:1;
|
||||||
|
|
@ -872,13 +873,18 @@ static int do_sync(struct client *client)
|
||||||
client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync);
|
client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (in_data_thread && client->rt_locked)
|
if (in_data_thread) {
|
||||||
pthread_mutex_unlock(&client->rt_lock);
|
if (client->rt_locked)
|
||||||
|
pthread_mutex_unlock(&client->rt_lock);
|
||||||
|
client->data_locked = true;
|
||||||
|
}
|
||||||
pw_thread_loop_wait(client->context.loop);
|
pw_thread_loop_wait(client->context.loop);
|
||||||
|
|
||||||
if (in_data_thread && client->rt_locked)
|
if (in_data_thread) {
|
||||||
pthread_mutex_lock(&client->rt_lock);
|
client->data_locked = false;
|
||||||
|
if (client->rt_locked)
|
||||||
|
pthread_mutex_lock(&client->rt_lock);
|
||||||
|
}
|
||||||
|
|
||||||
if (client->last_res < 0)
|
if (client->last_res < 0)
|
||||||
return client->last_res;
|
return client->last_res;
|
||||||
|
|
@ -927,25 +933,23 @@ static struct link *find_activation(struct spa_list *links, uint32_t node_id)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void client_remove_source(struct client *c)
|
||||||
|
{
|
||||||
|
if (c->socket_source) {
|
||||||
|
pw_loop_destroy_source(c->loop->loop, c->socket_source);
|
||||||
|
c->socket_source = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_remove_sources(struct spa_loop *loop,
|
do_remove_sources(struct spa_loop *loop,
|
||||||
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||||
{
|
{
|
||||||
struct client *c = user_data;
|
struct client *c = user_data;
|
||||||
|
client_remove_source(c);
|
||||||
if (c->socket_source) {
|
|
||||||
pw_loop_destroy_source(c->loop->loop, c->socket_source);
|
|
||||||
c->socket_source = NULL;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unhandle_socket(struct client *c)
|
|
||||||
{
|
|
||||||
pw_data_loop_invoke(c->loop,
|
|
||||||
do_remove_sources, 1, NULL, 0, true, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id)
|
static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id)
|
||||||
{
|
{
|
||||||
struct buffer *b;
|
struct buffer *b;
|
||||||
|
|
@ -1463,7 +1467,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask)
|
||||||
|
|
||||||
if (SPA_UNLIKELY(mask & (SPA_IO_ERR | SPA_IO_HUP))) {
|
if (SPA_UNLIKELY(mask & (SPA_IO_ERR | SPA_IO_HUP))) {
|
||||||
pw_log_warn("%p: got error", c);
|
pw_log_warn("%p: got error", c);
|
||||||
unhandle_socket(c);
|
client_remove_source(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SPA_UNLIKELY(c->thread_callback)) {
|
if (SPA_UNLIKELY(c->thread_callback)) {
|
||||||
|
|
@ -1496,7 +1500,7 @@ do_clear_link(struct spa_loop *loop,
|
||||||
static void clear_link(struct client *c, struct link *link)
|
static void clear_link(struct client *c, struct link *link)
|
||||||
{
|
{
|
||||||
pw_data_loop_invoke(c->loop,
|
pw_data_loop_invoke(c->loop,
|
||||||
do_clear_link, 1, NULL, 0, true, link);
|
do_clear_link, 1, NULL, 0, !c->data_locked, link);
|
||||||
pw_memmap_free(link->mem);
|
pw_memmap_free(link->mem);
|
||||||
close(link->signalfd);
|
close(link->signalfd);
|
||||||
spa_list_remove(&link->link);
|
spa_list_remove(&link->link);
|
||||||
|
|
@ -1510,7 +1514,8 @@ static void clean_transport(struct client *c)
|
||||||
if (!c->has_transport)
|
if (!c->has_transport)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unhandle_socket(c);
|
pw_data_loop_invoke(c->loop,
|
||||||
|
do_remove_sources, 1, NULL, 0, !c->data_locked, c);
|
||||||
|
|
||||||
spa_list_consume(l, &c->links, link)
|
spa_list_consume(l, &c->links, link)
|
||||||
clear_link(c, l);
|
clear_link(c, l);
|
||||||
|
|
@ -1636,7 +1641,8 @@ static int update_driver_activation(struct client *c)
|
||||||
link = find_activation(&c->links, c->driver_id);
|
link = find_activation(&c->links, c->driver_id);
|
||||||
c->driver_activation = link ? link->activation : NULL;
|
c->driver_activation = link ? link->activation : NULL;
|
||||||
pw_data_loop_invoke(c->loop,
|
pw_data_loop_invoke(c->loop,
|
||||||
do_update_driver_activation, SPA_ID_INVALID, NULL, 0, true, c);
|
do_update_driver_activation, SPA_ID_INVALID, NULL, 0,
|
||||||
|
!c->data_locked, c);
|
||||||
install_timeowner(c);
|
install_timeowner(c);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue