diff --git a/src/virtManager/baseclass.py b/src/virtManager/baseclass.py index 0f209311d..fc6d1a78b 100644 --- a/src/virtManager/baseclass.py +++ b/src/virtManager/baseclass.py @@ -19,6 +19,8 @@ # import os +import sys +import logging import gtk import gobject @@ -36,10 +38,50 @@ class vmmGObject(gobject.GObject): gobject.GObject.__init__(self) self.config = virtManager.config.running_config + self._gobject_handles = [] + self._gobject_timeouts = [] + self._gconf_handles = [] + + def cleanup(self): + # Do any cleanup required to drop reference counts so object is + # actually reaped by python. Usually means unregistering callbacks + try: + for h in self._gconf_handles[:]: + self.remove_gconf_handle(h) + for h in self._gobject_handles[:]: + self.remove_gobject_handle(h) + for h in self._gobject_timeouts[:]: + self.remove_gobject_timeout(h) + except: + logging.exception("Error cleaning up %s" % self) + + def add_gconf_handle(self, handle): + self._gconf_handles.append(handle) + def remove_gconf_handle(self, handle): + self.config.remove_notifier(handle) + self._gconf_handles.remove(handle) + + def add_gobject_handle(self, handle): + self._gobject_handles.append(handle) + def remove_gobject_handle(self, handle): + self.disconnect(handle) + self._gobject_handles.remove(handle) + + def add_gobject_timeout(self, handle): + self._gobject_timeouts.append(handle) + def remove_gobject_timeout(self, handle): + gobject.source_remove(handle) + self._gobject_timeouts.remove(handle) + + def refcount(self): + # Function generates 2 temporary refs, so adjust total accordingly + return (sys.getrefcount(self) - 2) + def get_hal_helper(self): from virtManager import halhelper return halhelper.get_hal_helper() + class vmmGObjectUI(vmmGObject): def __init__(self, filename, windowname): vmmGObject.__init__(self) diff --git a/src/virtManager/config.py b/src/virtManager/config.py index f551a065e..39761ef40 100644 --- a/src/virtManager/config.py +++ b/src/virtManager/config.py @@ -176,6 +176,9 @@ class vmmConfig(object): ret.append("spice") return ret + def remove_notifier(self, h): + self.conf.notify_remove(h) + # Per-VM/Connection/Connection Host Option dealings def _perconn_helper(self, uri, pref_func, func_type, value=None): suffix = "connection_prefs/%s" % gconf.escape_key(uri, len(uri)) @@ -436,11 +439,11 @@ class vmmConfig(object): self.conf.set_bool(self.conf_dir + "/stats/enable-net-poll", val) def on_stats_enable_disk_poll_changed(self, cb, userdata=None): - self.conf.notify_add(self.conf_dir + "/stats/enable-disk-poll", cb, - userdata) + return self.conf.notify_add(self.conf_dir + "/stats/enable-disk-poll", + cb, userdata) def on_stats_enable_net_poll_changed(self, cb, userdata=None): - self.conf.notify_add(self.conf_dir + "/stats/enable-net-poll", cb, - userdata) + return self.conf.notify_add(self.conf_dir + "/stats/enable-net-poll", + cb, userdata) # VM Console preferences def on_console_popup_changed(self, callback): diff --git a/src/virtManager/connection.py b/src/virtManager/connection.py index f651395dd..a9d3e9bcf 100644 --- a/src/virtManager/connection.py +++ b/src/virtManager/connection.py @@ -890,16 +890,34 @@ class vmmConnection(vmmGObject): if self.vmm == None: return + def cleanup(devs): + for dev in devs.values(): + dev.cleanup() + self.vmm = None - self.nodedevs = {} - self.netdevs = {} - self.mediadevs = {} - self.interfaces = {} - self.pools = {} - self.nets = {} - self.vms = {} self.record = [] + cleanup(self.nodedevs) + self.nodedevs = {} + + cleanup(self.netdevs) + self.netdevs = {} + + cleanup(self.mediadevs) + self.mediadevs = {} + + cleanup(self.interfaces) + self.interfaces = {} + + cleanup(self.pools) + self.pools = {} + + cleanup(self.nets) + self.nets = {} + + cleanup(self.vms) + self.vms = {} + self._change_state(self.STATE_DISCONNECTED) def _open_dev_conn(self, uri): diff --git a/src/virtManager/domain.py b/src/virtManager/domain.py index 9b91ab387..52e766a5c 100644 --- a/src/virtManager/domain.py +++ b/src/virtManager/domain.py @@ -966,11 +966,6 @@ class vmmDomain(vmmDomainBase): self.lastStatus = libvirt.VIR_DOMAIN_SHUTOFF - self.config.on_stats_enable_net_poll_changed( - self.toggle_sample_network_traffic) - self.config.on_stats_enable_disk_poll_changed( - self.toggle_sample_disk_io) - self.getvcpus_supported = support.check_domain_support(self._backend, support.SUPPORT_DOMAIN_GETVCPUS) self.managedsave_supported = self.connection.get_dom_managedsave_supported(self._backend) @@ -987,13 +982,22 @@ class vmmDomain(vmmDomainBase): # Determine available XML flags (older libvirt versions will error # out if passed SECURE_XML, INACTIVE_XML, etc) (self._inactive_xml_flags, - self._active_xml_flags) = self.connection.get_dom_flags( - self._backend) + self._active_xml_flags) = self.connection.get_dom_flags(self._backend) - # Hook up our own status listeners self._update_status() - self.connect("status-changed", self._update_start_vcpus) - self.connect("config-changed", self._reparse_xml) + + # Hook up listeners that need to be cleaned up + self.add_gconf_handle( + self.config.on_stats_enable_net_poll_changed( + self.toggle_sample_network_traffic)) + self.add_gconf_handle( + self.config.on_stats_enable_disk_poll_changed( + self.toggle_sample_disk_io)) + + self.add_gobject_handle( + self.connect("status-changed", self._update_start_vcpus)) + self.add_gobject_handle( + self.connect("config-changed", self._reparse_xml)) ########################## # Internal virDomain API # diff --git a/src/virtManager/mediadev.py b/src/virtManager/mediadev.py index 057f729a2..117d9bcc7 100644 --- a/src/virtManager/mediadev.py +++ b/src/virtManager/mediadev.py @@ -142,8 +142,10 @@ class vmmMediaDevice(vmmGObject): self.poll_signal = util.safe_timeout_add(MEDIA_TIMEOUT * 1000, self._poll_for_media) + self.add_gobject_timeout(self.poll_signal) def disable_poll_for_media(self): + self.remove_gobject_timeout(self.poll_signal) self.poll_signal = None def _poll_for_media(self):