domain: Fix ref count issues preventing proper cleanup

Make sure we unregister all callbacks so that the domain object
is properly reaped by python. Mediadev has somewhat similar issues,
so handle that.
This commit is contained in:
Cole Robinson 2011-04-11 11:00:57 -04:00
parent f5464b606d
commit 586f7b3274
5 changed files with 90 additions and 21 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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 #

View File

@ -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):