daemon: fix port lifecycle problem that was causing crash

There are three types of ports depeneding on references to them:
 * ports that are referenced from jack and studio graphs
 * ports that are referenced from jack and room graphs
 * studio-room link ports that are referenced from studio and room graphs

The old approach was to destroy ports when removing references from jack graph.
Studio-room link ports were destroyed when removing the room.
However this caused double free when removing app ports that appear in jack and
room graphs.

The introduced implementation uses reference counting of graph references to ports.
Temporary references (like the one returned by port constructor and graph_get_port())
are not tracked.
This commit is contained in:
Nedko Arnaudov 2010-04-04 21:03:59 +03:00
parent d161a68191
commit de772730a5
8 changed files with 51 additions and 35 deletions

View File

@ -36,8 +36,8 @@ static bool run(void * context)
ladish_graph_dump(g_studio.studio_graph);
ladish_graph_dump(g_studio.jack_graph);
ladish_graph_clear(g_studio.studio_graph, false);
ladish_graph_clear(g_studio.jack_graph, true);
ladish_graph_clear(g_studio.studio_graph);
ladish_graph_clear(g_studio.jack_graph);
studio_remove_all_rooms();

View File

@ -873,13 +873,9 @@ void
ladish_graph_remove_port_internal(
struct ladish_graph * graph_ptr,
struct ladish_graph_client * client_ptr,
struct ladish_graph_port * port_ptr,
bool destroy)
struct ladish_graph_port * port_ptr)
{
if (destroy)
{
ladish_port_destroy(port_ptr->port);
}
ladish_port_del_ref(port_ptr->port);
list_del(&port_ptr->siblings_client);
list_del(&port_ptr->siblings_graph);
@ -909,15 +905,14 @@ void
ladish_graph_remove_client_internal(
struct ladish_graph * graph_ptr,
struct ladish_graph_client * client_ptr,
bool destroy_client,
bool destroy_ports)
bool destroy_client)
{
struct ladish_graph_port * port_ptr;
while (!list_empty(&client_ptr->ports))
{
port_ptr = list_entry(client_ptr->ports.next, struct ladish_graph_port, siblings_client);
ladish_graph_remove_port_internal(graph_ptr, client_ptr, port_ptr, destroy_ports);
ladish_graph_remove_port_internal(graph_ptr, client_ptr, port_ptr);
}
graph_ptr->graph_version++;
@ -948,9 +943,9 @@ ladish_graph_remove_client_internal(
#define graph_ptr ((struct ladish_graph *)graph_handle)
void ladish_graph_destroy(ladish_graph_handle graph_handle, bool destroy_ports)
void ladish_graph_destroy(ladish_graph_handle graph_handle)
{
ladish_graph_clear(graph_handle, destroy_ports);
ladish_graph_clear(graph_handle);
ladish_dict_destroy(graph_ptr->dict);
if (graph_ptr->opath != NULL)
{
@ -976,7 +971,7 @@ ladish_graph_set_connection_handlers(
graph_ptr->disconnect_handler = disconnect_handler;
}
void ladish_graph_clear(ladish_graph_handle graph_handle, bool destroy_ports)
void ladish_graph_clear(ladish_graph_handle graph_handle)
{
struct ladish_graph_client * client_ptr;
struct ladish_graph_connection * connection_ptr;
@ -992,7 +987,7 @@ void ladish_graph_clear(ladish_graph_handle graph_handle, bool destroy_ports)
while (!list_empty(&graph_ptr->clients))
{
client_ptr = list_entry(graph_ptr->clients.next, struct ladish_graph_client, siblings);
ladish_graph_remove_client_internal(graph_ptr, client_ptr, true, destroy_ports);
ladish_graph_remove_client_internal(graph_ptr, client_ptr, true);
}
}
@ -1199,8 +1194,7 @@ bool ladish_graph_add_client(ladish_graph_handle graph_handle, ladish_client_han
void
ladish_graph_remove_client(
ladish_graph_handle graph_handle,
ladish_client_handle client_handle,
bool destroy_ports)
ladish_client_handle client_handle)
{
struct ladish_graph_client * client_ptr;
@ -1209,7 +1203,7 @@ ladish_graph_remove_client(
client_ptr = ladish_graph_find_client(graph_ptr, client_handle);
if (client_ptr != NULL)
{
ladish_graph_remove_client_internal(graph_ptr, client_ptr, false, destroy_ports);
ladish_graph_remove_client_internal(graph_ptr, client_ptr, false);
}
else
{
@ -1260,6 +1254,7 @@ ladish_graph_add_port(
port_ptr->id = graph_ptr->next_port_id++;
port_ptr->port = port_handle;
ladish_port_add_ref(port_ptr->port);
port_ptr->hidden = true;
port_ptr->client_ptr = client_ptr;
@ -1627,7 +1622,7 @@ ladish_graph_remove_port(
return NULL;
}
ladish_graph_remove_port_internal(graph_ptr, port_ptr->client_ptr, port_ptr, false);
ladish_graph_remove_port_internal(graph_ptr, port_ptr->client_ptr, port_ptr);
return port_ptr->client_ptr->client;
}

View File

@ -49,7 +49,7 @@ bool
bool ladish_graph_create(ladish_graph_handle * graph_handle_ptr, const char * opath);
bool ladish_graph_copy(ladish_graph_handle src, ladish_graph_handle dest);
void ladish_graph_destroy(ladish_graph_handle graph_handle, bool destroy_ports);
void ladish_graph_destroy(ladish_graph_handle graph_handle);
const char * ladish_graph_get_opath(ladish_graph_handle graph_handle);
@ -60,7 +60,7 @@ ladish_graph_set_connection_handlers(
ladish_graph_connect_request_handler connect_handler,
ladish_graph_disconnect_request_handler disconnect_handler);
void ladish_graph_clear(ladish_graph_handle graph_handle, bool destroy_ports);
void ladish_graph_clear(ladish_graph_handle graph_handle);
void * ladish_graph_get_dbus_context(ladish_graph_handle graph_handle);
ladish_dict_handle ladish_graph_get_dict(ladish_graph_handle graph_handle);
ladish_dict_handle ladish_graph_get_connection_dict(ladish_graph_handle graph_handle, uint64_t connection_id);
@ -69,8 +69,7 @@ bool ladish_graph_add_client(ladish_graph_handle graph_handle, ladish_client_han
void
ladish_graph_remove_client(
ladish_graph_handle graph_handle,
ladish_client_handle client_handle,
bool destroy_ports);
ladish_client_handle client_handle);
bool
ladish_graph_rename_client(

View File

@ -29,6 +29,7 @@
/* JACK port or virtual port */
struct ladish_port
{
int refcount;
uuid_t uuid; /* The UUID of the port */
bool virtual; /* Whether the port is virtual or JACK port */
uint64_t jack_id; /* JACK port ID. zero for virtual ports. */
@ -68,6 +69,7 @@ ladish_port_create(
port_ptr->jack_id = 0;
port_ptr->virtual = true;
port_ptr->refcount = 0;
log_info("port %p created", port_ptr);
*port_handle_ptr = (ladish_port_handle)port_ptr;
@ -84,6 +86,7 @@ bool ladish_port_create_copy(ladish_port_handle port_handle, ladish_port_handle
void ladish_port_destroy(ladish_port_handle port_handle)
{
log_info("port %p destroy", port_ptr);
ASSERT(port_ptr->refcount == 0);
ladish_dict_destroy(port_ptr->dict);
free(port_ptr);
}
@ -109,4 +112,20 @@ uint64_t ladish_port_get_jack_id(ladish_port_handle port_handle)
return port_ptr->jack_id;
}
void ladish_port_add_ref(ladish_port_handle port_handle)
{
port_ptr->refcount++;
}
void ladish_port_del_ref(ladish_port_handle port_handle)
{
ASSERT(port_ptr->refcount > 0);
port_ptr->refcount--;
if (port_ptr->refcount == 0)
{
ladish_port_destroy(port_handle);
}
}
#undef port_ptr

View File

@ -39,4 +39,7 @@ void ladish_port_get_uuid(ladish_port_handle port_handle, uuid_t uuid);
void ladish_port_set_jack_id(ladish_port_handle port_handle, uint64_t jack_id);
uint64_t ladish_port_get_jack_id(ladish_port_handle port_handle);
void ladish_port_add_ref(ladish_port_handle port_handle);
void ladish_port_del_ref(ladish_port_handle port_handle);
#endif /* #ifndef PORT_H__62F81E7C_91FA_44AB_94A9_E0E2D226ED58__INCLUDED */

View File

@ -152,7 +152,7 @@ destroy_dbus_object:
destroy_app_supervisor:
ladish_app_supervisor_destroy(room_ptr->app_supervisor);
destroy_graph:
ladish_graph_destroy(room_ptr->graph, true);
ladish_graph_destroy(room_ptr->graph);
free_name:
free(room_ptr->name);
free_room:
@ -177,7 +177,7 @@ ladish_room_destroy(
ladish_app_supervisor_destroy(room_ptr->app_supervisor);
}
ladish_graph_destroy(room_ptr->graph, true);
ladish_graph_destroy(room_ptr->graph);
free(room_ptr->name);
free(room_ptr);
}

View File

@ -355,8 +355,8 @@ void studio_run(void)
log_error("Save your work, then unload and reload the studio.");
ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
ladish_graph_clear(g_studio.studio_graph, false);
ladish_graph_clear(g_studio.jack_graph, true);
ladish_graph_clear(g_studio.studio_graph);
ladish_graph_clear(g_studio.jack_graph);
handle_unexpected_jack_server_stop();
}
@ -477,9 +477,9 @@ bool studio_init(void)
app_supervisor_destroy:
ladish_app_supervisor_destroy(g_studio.app_supervisor);
studio_graph_destroy:
ladish_graph_destroy(g_studio.studio_graph, false);
ladish_graph_destroy(g_studio.studio_graph);
jack_graph_destroy:
ladish_graph_destroy(g_studio.jack_graph, false);
ladish_graph_destroy(g_studio.jack_graph);
free_studios_dir:
free(g_studios_dir);
fail:
@ -494,8 +494,8 @@ void studio_uninit(void)
ladish_cqueue_clear(&g_studio.cmd_queue);
ladish_graph_destroy(g_studio.studio_graph, false);
ladish_graph_destroy(g_studio.jack_graph, false);
ladish_graph_destroy(g_studio.studio_graph);
ladish_graph_destroy(g_studio.jack_graph);
free(g_studios_dir);
@ -978,7 +978,7 @@ static void ladish_studio_create_room(struct dbus_method_call * call_ptr)
return;
fail_remove_room_client:
ladish_graph_remove_client(g_studio.studio_graph, room_client, false);
ladish_graph_remove_client(g_studio.studio_graph, room_client);
fail_destroy_room_client:
ladish_client_destroy(room_client);
fail_destroy_room:
@ -1067,7 +1067,7 @@ static void ladish_studio_delete_room(struct dbus_method_call * call_ptr)
room_client = ladish_graph_find_client_by_name(g_studio.studio_graph, ladish_room_get_name(room));
ASSERT(room_client != NULL);
ladish_graph_remove_client(g_studio.studio_graph, room_client, false);
ladish_graph_remove_client(g_studio.studio_graph, room_client);
ladish_client_destroy(room_client);
ladish_room_destroy(room);

View File

@ -306,7 +306,7 @@ static void client_disappeared(void * context, uint64_t id)
}
else
{
ladish_graph_remove_client(virtualizer_ptr->jack_graph, client, false);
ladish_graph_remove_client(virtualizer_ptr->jack_graph, client);
ladish_client_destroy(client);
}
}
@ -611,7 +611,7 @@ static void port_disappeared(void * context, uint64_t client_id, uint64_t port_i
{
if (ladish_graph_is_client_empty(vgraph, client))
{
ladish_graph_remove_client(vgraph, client, false);
ladish_graph_remove_client(vgraph, client);
}
}
}