bus-manager: fix handling of incoming signals

The 'new-connection' signal of a GDBusServer is emitted by default
through an idle source and the actual message processing starts only
after a signal handler returns TRUE.

Thus, before the signal handler has the chance to run, the GDBus
worker thread may detect that the connection is closed and schedule
the delivery of the 'closed' signal through another idle source.

After the termination of the 'new-connection' handler, the 'closed'
handler is executed, which cancels the subscription to GDBus signals
before any message has been processed.

This looks like a bug in GDBusServer; to work around it, just delay
the close of connection to let the signal dispatch run first.

https://bugzilla.gnome.org/show_bug.cgi?id=755170
This commit is contained in:
Beniamino Galvani 2015-09-30 16:26:38 +02:00
parent 18b20c4f6f
commit 7c5518ac8d

View file

@ -166,6 +166,38 @@ struct _PrivateServer {
NMBusManager *manager;
};
typedef struct {
GDBusConnection *connection;
PrivateServer *server;
gboolean remote_peer_vanished;
} CloseConnectionInfo;
static gboolean
close_connection_in_idle (gpointer user_data)
{
CloseConnectionInfo *info = user_data;
PrivateServer *server = info->server;
/* Emit this for the manager */
g_signal_emit (server->manager,
signals[PRIVATE_CONNECTION_DISCONNECTED],
server->detail,
info->connection);
/* FIXME: there's a bug (754730) in GLib for which the connection
* is marked as closed when the remote peer vanishes but its
* resources are not cleaned up. Work around it by explicitly
* closing the connection in that case. */
if (info->remote_peer_vanished)
g_dbus_connection_close (info->connection, NULL, NULL, NULL);
g_hash_table_remove (server->connections, info->connection);
g_object_unref (server->manager);
g_slice_free (CloseConnectionInfo, info);
return G_SOURCE_REMOVE;
}
static void
private_server_closed (GDBusConnection *conn,
gboolean remote_peer_vanished,
@ -173,25 +205,22 @@ private_server_closed (GDBusConnection *conn,
gpointer user_data)
{
PrivateServer *s = user_data;
CloseConnectionInfo *info;
/* Clean up after the connection */
nm_log_dbg (LOGD_CORE, "(%s) closed connection %p on private socket.",
s->tag, conn);
/* Emit this for the manager */
g_signal_emit (s->manager,
signals[PRIVATE_CONNECTION_DISCONNECTED],
s->detail,
conn);
info = g_slice_new0 (CloseConnectionInfo);
info->connection = conn;
info->server = s;
info->remote_peer_vanished = remote_peer_vanished;
/* FIXME: there's a bug (754730) in GLib for which the connection
* is marked as closed when the remote peer vanishes but its
* resources are not cleaned up. Work around it by explicitly
* closing the connection in that case. */
if (remote_peer_vanished)
g_dbus_connection_close (conn, NULL, NULL, NULL);
g_object_ref (s->manager);
g_hash_table_remove (s->connections, conn);
/* Delay the close of connection to ensure that D-Bus signals
* are handled */
g_idle_add (close_connection_in_idle, info);
}
static gboolean