There are two parts to this fix. First, create a private DBusConnection manually, instead of using nih_dbus_connect. The latter always creates a shared connection, which cannot be closed. Note: creating an actual shared connection, mutexing it among all threads, and creating per-thread proxies would be an alternative - however we don't want long-lived connections as they tend not to be reliable (especially if cgmanager restarts).
Second, use pthread_setspecific to create per-thread keys which can be associated with destructors. Specify a destructor which closes the dbus connection. If a thread dies while holding cgmanager, the connection will be closed. Otherwise, we close the connection and unset the key. Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- src/lxc/cgmanager.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c index c80a136..4cf6397 100644 --- a/src/lxc/cgmanager.c +++ b/src/lxc/cgmanager.c @@ -70,10 +70,42 @@ struct cgm_data { static __thread NihDBusProxy *cgroup_manager = NULL; static __thread DBusConnection *connection = NULL; static __thread bool cgm_keep_connection = false; + + +struct cgm_key_t { + DBusConnection *connection; + NihDBusProxy *cgmanager; +} cgm_key; + +void destructor(void *arg) { + struct cgm_key_t *cgm_key = arg; + nih_free(cgm_key->cgmanager); + dbus_connection_flush(cgm_key->connection); + dbus_connection_close(cgm_key->connection); + dbus_connection_unref(cgm_key->connection); +} +static pthread_key_t key; +static void make_key() { + pthread_key_create(&key, destructor); +} + +static void init_cgm_destructor(DBusConnection *c, NihDBusProxy *cgm) +{ + static pthread_once_t key_once = PTHREAD_ONCE_INIT; + pthread_once(&key_once, make_key); + cgm_key.connection = c; + cgm_key.cgmanager = cgm; + if (!cgm) + pthread_setspecific(key, NULL); + else if (pthread_getspecific(key) == NULL) + pthread_setspecific(key, &cgm_key); +} + #else static NihDBusProxy *cgroup_manager = NULL; static DBusConnection *connection = NULL; static bool cgm_keep_connection = false; +static inline void init_cgm_destructor(DBusConnection *c, NihDBusProxy *cgm) { } #endif static struct cgroup_ops cgmanager_ops; @@ -87,9 +119,13 @@ static void cgm_dbus_disconnect(void) if (cgroup_manager) nih_free(cgroup_manager); cgroup_manager = NULL; - if (connection) + if (connection) { + dbus_connection_flush(connection); + dbus_connection_close(connection); dbus_connection_unref(connection); + } connection = NULL; + init_cgm_destructor(NULL, NULL); } #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock" @@ -105,14 +141,22 @@ static bool do_cgm_dbus_connect(void) dbus_error_init(&dbus_error); - connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, NULL); + connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error); if (!connection) { + ERROR("Failed opening dbus connection: %s: %s", + dbus_error.name, dbus_error.message); + dbus_error_free(&dbus_error); + return false; + } + if (nih_dbus_setup(connection, NULL) < 0) { NihError *nerr; nerr = nih_error_get(); DEBUG("Unable to open cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK, nerr->message); nih_free(nerr); dbus_error_free(&dbus_error); + dbus_connection_unref(connection); + connection = NULL; return false; } dbus_connection_set_exit_on_disconnect(connection, FALSE); @@ -138,6 +182,7 @@ static bool do_cgm_dbus_connect(void) cgm_dbus_disconnect(); return false; } + init_cgm_destructor(connection, cgroup_manager); return true; } -- 1.9.0 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel