mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-23 17:34:21 +03:00
Make sure all idle and timeout routines are thread safe.
Unbeknownst to me, these functions are not run thread safe: http://library.gnome.org/devel/gdk/unstable/gdk-Threads.html However since they are run from the main loop, the chance of them conflicting with another running thread is slim, since we have very few threads that actually update the UI.
This commit is contained in:
parent
494a93cccc
commit
dfb8f8666a
@ -25,6 +25,8 @@ import gtk.gdk
|
|||||||
import gtk.glade
|
import gtk.glade
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
|
from virtManager import util
|
||||||
|
|
||||||
# Displays a progress bar while executing the "callback" method.
|
# Displays a progress bar while executing the "callback" method.
|
||||||
|
|
||||||
class vmmAsyncJob(gobject.GObject):
|
class vmmAsyncJob(gobject.GObject):
|
||||||
@ -61,7 +63,7 @@ class vmmAsyncJob(gobject.GObject):
|
|||||||
self.is_pulsing = True
|
self.is_pulsing = True
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
timer = gobject.timeout_add (100, self.exit_if_necessary)
|
timer = util.safe_timeout_add(100, self.exit_if_necessary)
|
||||||
self.topwin.present()
|
self.topwin.present()
|
||||||
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
||||||
self.bg_thread.start()
|
self.bg_thread.start()
|
||||||
@ -74,7 +76,7 @@ class vmmAsyncJob(gobject.GObject):
|
|||||||
# async dialog is running. This forces us to clean up properly
|
# async dialog is running. This forces us to clean up properly
|
||||||
# and not leave a dead process around.
|
# and not leave a dead process around.
|
||||||
logging.debug("Forcing main_quit from async job.")
|
logging.debug("Forcing main_quit from async job.")
|
||||||
self._exit_if_necessary(force_exit=True)
|
self.exit_if_necessary(force_exit=True)
|
||||||
|
|
||||||
self.topwin.destroy()
|
self.topwin.destroy()
|
||||||
|
|
||||||
@ -132,16 +134,10 @@ class vmmAsyncJob(gobject.GObject):
|
|||||||
return (None, None)
|
return (None, None)
|
||||||
return self._error_info
|
return self._error_info
|
||||||
|
|
||||||
def exit_if_necessary(self):
|
def exit_if_necessary(self, force_exit=False):
|
||||||
gtk.gdk.threads_enter()
|
|
||||||
try:
|
|
||||||
return self._exit_if_necessary()
|
|
||||||
finally:
|
|
||||||
gtk.gdk.threads_leave()
|
|
||||||
|
|
||||||
def _exit_if_necessary(self, force_exit=False):
|
|
||||||
if self.bg_thread.isAlive() and not force_exit:
|
if self.bg_thread.isAlive() and not force_exit:
|
||||||
if (self.is_pulsing):
|
if (self.is_pulsing):
|
||||||
|
# Don't call pulse_pbar: this function is thread wrapped
|
||||||
self.pbar.pulse()
|
self.pbar.pulse()
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
@ -966,14 +966,14 @@ class vmmConnection(gobject.GObject):
|
|||||||
# We want to kill off this thread asap, so schedule a gobject
|
# We want to kill off this thread asap, so schedule a gobject
|
||||||
# idle even to inform the UI of result
|
# idle even to inform the UI of result
|
||||||
logging.debug("Background open thread complete, scheduling notify")
|
logging.debug("Background open thread complete, scheduling notify")
|
||||||
gobject.idle_add(self._open_notify)
|
util.safe_idle_add(self._open_notify)
|
||||||
self.connectThread = None
|
self.connectThread = None
|
||||||
|
|
||||||
def _open_notify(self):
|
def _open_notify(self):
|
||||||
logging.debug("Notifying open result")
|
logging.debug("Notifying open result")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gobject.idle_add(util.idle_emit, self, "state-changed")
|
util.safe_idle_add(util.idle_emit, self, "state-changed")
|
||||||
|
|
||||||
if self.state == self.STATE_ACTIVE:
|
if self.state == self.STATE_ACTIVE:
|
||||||
caps = self.get_capabilities_xml()
|
caps = self.get_capabilities_xml()
|
||||||
@ -988,7 +988,7 @@ class vmmConnection(gobject.GObject):
|
|||||||
self.vms.keys())
|
self.vms.keys())
|
||||||
|
|
||||||
if self.state == self.STATE_DISCONNECTED:
|
if self.state == self.STATE_DISCONNECTED:
|
||||||
gobject.idle_add(util.idle_emit, self, "connect-error",
|
util.safe_idle_add(util.idle_emit, self, "connect-error",
|
||||||
self.connectError)
|
self.connectError)
|
||||||
self.connectError = None
|
self.connectError = None
|
||||||
finally:
|
finally:
|
||||||
@ -1445,7 +1445,7 @@ class vmmConnection(gobject.GObject):
|
|||||||
for name in newNodedevs:
|
for name in newNodedevs:
|
||||||
self.emit("nodedev-added", self.uri, name)
|
self.emit("nodedev-added", self.uri, name)
|
||||||
|
|
||||||
gobject.idle_add(tick_send_signals)
|
util.safe_idle_add(tick_send_signals)
|
||||||
|
|
||||||
# Finally, we sample each domain
|
# Finally, we sample each domain
|
||||||
now = time()
|
now = time()
|
||||||
@ -1468,7 +1468,7 @@ class vmmConnection(gobject.GObject):
|
|||||||
if not noStatsUpdate:
|
if not noStatsUpdate:
|
||||||
self._recalculate_stats(now)
|
self._recalculate_stats(now)
|
||||||
|
|
||||||
gobject.idle_add(util.idle_emit, self, "resources-sampled")
|
util.safe_idle_add(util.idle_emit, self, "resources-sampled")
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import socket
|
|||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from virtManager import util
|
||||||
from virtManager.error import vmmErrorDialog
|
from virtManager.error import vmmErrorDialog
|
||||||
|
|
||||||
# Console pages
|
# Console pages
|
||||||
@ -477,7 +478,7 @@ class vmmConsolePages(gobject.GObject):
|
|||||||
logging.error("Too many connection failures, not retrying again")
|
logging.error("Too many connection failures, not retrying again")
|
||||||
return
|
return
|
||||||
logging.warn("Retrying connection in %d ms", self.vncViewerRetryDelay)
|
logging.warn("Retrying connection in %d ms", self.vncViewerRetryDelay)
|
||||||
gobject.timeout_add(self.vncViewerRetryDelay, self.retry_login)
|
util.safe_timeout_add(self.vncViewerRetryDelay, self.retry_login)
|
||||||
if self.vncViewerRetryDelay < 2000:
|
if self.vncViewerRetryDelay < 2000:
|
||||||
self.vncViewerRetryDelay = self.vncViewerRetryDelay * 2
|
self.vncViewerRetryDelay = self.vncViewerRetryDelay * 2
|
||||||
|
|
||||||
@ -489,12 +490,8 @@ class vmmConsolePages(gobject.GObject):
|
|||||||
libvirt.VIR_DOMAIN_CRASHED ]:
|
libvirt.VIR_DOMAIN_CRASHED ]:
|
||||||
return
|
return
|
||||||
|
|
||||||
gtk.gdk.threads_enter()
|
|
||||||
try:
|
|
||||||
self.try_login()
|
self.try_login()
|
||||||
return
|
return
|
||||||
finally:
|
|
||||||
gtk.gdk.threads_leave()
|
|
||||||
|
|
||||||
def open_tunnel(self, server, vncaddr, vncport, username, sshport):
|
def open_tunnel(self, server, vncaddr, vncport, username, sshport):
|
||||||
if self.vncTunnel is not None:
|
if self.vncTunnel is not None:
|
||||||
@ -676,7 +673,7 @@ class vmmConsolePages(gobject.GObject):
|
|||||||
|
|
||||||
def unset_cb(src):
|
def unset_cb(src):
|
||||||
src.queue_resize_no_redraw()
|
src.queue_resize_no_redraw()
|
||||||
gobject.idle_add(restore_scroll, src)
|
util.safe_idle_add(restore_scroll, src)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def request_cb(src, req):
|
def request_cb(src, req):
|
||||||
@ -686,7 +683,7 @@ class vmmConsolePages(gobject.GObject):
|
|||||||
|
|
||||||
src.disconnect(signal_id)
|
src.disconnect(signal_id)
|
||||||
|
|
||||||
gobject.idle_add(unset_cb, widget)
|
util.safe_idle_add(unset_cb, widget)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Disable scroll bars while we resize, since resizing to the VM's
|
# Disable scroll bars while we resize, since resizing to the VM's
|
||||||
|
@ -349,7 +349,7 @@ class vmmCreate(gobject.GObject):
|
|||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
if not self.host_storage_timer:
|
if not self.host_storage_timer:
|
||||||
self.host_storage_timer = gobject.timeout_add(3 * 1000,
|
self.host_storage_timer = util.safe_timeout_add(3 * 1000,
|
||||||
self.host_space_tick)
|
self.host_space_tick)
|
||||||
self.window.get_widget("enable-storage").set_active(True)
|
self.window.get_widget("enable-storage").set_active(True)
|
||||||
self.window.get_widget("config-storage-create").set_active(True)
|
self.window.get_widget("config-storage-create").set_active(True)
|
||||||
|
@ -1357,7 +1357,7 @@ class vmmDomain(vmmDomainBase):
|
|||||||
if origxml != self._xml:
|
if origxml != self._xml:
|
||||||
# 'tick' to make sure we have the latest time
|
# 'tick' to make sure we have the latest time
|
||||||
self.tick(time.time())
|
self.tick(time.time())
|
||||||
gobject.idle_add(util.idle_emit, self, "config-changed")
|
util.safe_idle_add(util.idle_emit, self, "config-changed")
|
||||||
|
|
||||||
def _redefine(self, xml_func, *args):
|
def _redefine(self, xml_func, *args):
|
||||||
"""
|
"""
|
||||||
@ -1848,7 +1848,7 @@ class vmmDomain(vmmDomainBase):
|
|||||||
self._startup_vcpus = None
|
self._startup_vcpus = None
|
||||||
self.vcpu_max_count()
|
self.vcpu_max_count()
|
||||||
self.lastStatus = status
|
self.lastStatus = status
|
||||||
gobject.idle_add(util.idle_emit, self, "status-changed", status)
|
util.safe_idle_add(util.idle_emit, self, "status-changed", status)
|
||||||
|
|
||||||
|
|
||||||
def tick(self, now):
|
def tick(self, now):
|
||||||
@ -1917,7 +1917,7 @@ class vmmDomain(vmmDomainBase):
|
|||||||
|
|
||||||
self.record.insert(0, newStats)
|
self.record.insert(0, newStats)
|
||||||
self._update_status(info[0])
|
self._update_status(info[0])
|
||||||
gobject.idle_add(util.idle_emit, self, "resources-sampled")
|
util.safe_idle_add(util.idle_emit, self, "resources-sampled")
|
||||||
|
|
||||||
|
|
||||||
class vmmDomainVirtinst(vmmDomainBase):
|
class vmmDomainVirtinst(vmmDomainBase):
|
||||||
|
@ -191,6 +191,8 @@ class vmmEngine(gobject.GObject):
|
|||||||
gobject.source_remove(self.timer)
|
gobject.source_remove(self.timer)
|
||||||
self.timer = None
|
self.timer = None
|
||||||
|
|
||||||
|
# No need to use 'safe_timeout_add', the tick should be
|
||||||
|
# manually made thread safe
|
||||||
self.timer = gobject.timeout_add(interval, self.tick)
|
self.timer = gobject.timeout_add(interval, self.tick)
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
@ -205,7 +207,7 @@ class vmmEngine(gobject.GObject):
|
|||||||
|
|
||||||
self._tick_thread = threading.Thread(name="Tick thread",
|
self._tick_thread = threading.Thread(name="Tick thread",
|
||||||
target=self._tick, args=())
|
target=self._tick, args=())
|
||||||
self._tick_thread.daemon = False
|
self._tick_thread.daemon = True
|
||||||
self._tick_thread.start()
|
self._tick_thread.start()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@ -221,7 +223,7 @@ class vmmEngine(gobject.GObject):
|
|||||||
logging.exception("Could not refresh connection %s." % uri)
|
logging.exception("Could not refresh connection %s." % uri)
|
||||||
logging.debug("Closing connection since libvirtd "
|
logging.debug("Closing connection since libvirtd "
|
||||||
"appears to have stopped.")
|
"appears to have stopped.")
|
||||||
gobject.idle_add(conn.close)
|
util.safe_idle_add(conn.close)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
return 1
|
return 1
|
||||||
|
@ -23,6 +23,8 @@ import logging
|
|||||||
|
|
||||||
import virtinst
|
import virtinst
|
||||||
|
|
||||||
|
from virtManager import util
|
||||||
|
|
||||||
MEDIA_FLOPPY = "floppy"
|
MEDIA_FLOPPY = "floppy"
|
||||||
MEDIA_CDROM = "cdrom"
|
MEDIA_CDROM = "cdrom"
|
||||||
|
|
||||||
@ -136,7 +138,7 @@ class vmmMediaDevice(gobject.GObject):
|
|||||||
if self.poll_signal:
|
if self.poll_signal:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.poll_signal = gobject.timeout_add(MEDIA_TIMEOUT * 1000,
|
self.poll_signal = util.safe_timeout_add(MEDIA_TIMEOUT * 1000,
|
||||||
self._poll_for_media)
|
self._poll_for_media)
|
||||||
|
|
||||||
def disable_poll_for_media(self):
|
def disable_poll_for_media(self):
|
||||||
|
@ -18,12 +18,14 @@
|
|||||||
# MA 02110-1301 USA.
|
# MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
|
||||||
import gtk
|
import gtk
|
||||||
import libxml2
|
import gobject
|
||||||
import os.path
|
|
||||||
|
|
||||||
import libvirt
|
import libvirt
|
||||||
|
import libxml2
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os.path
|
||||||
|
|
||||||
import virtManager
|
import virtManager
|
||||||
import virtinst
|
import virtinst
|
||||||
@ -245,6 +247,25 @@ def idle_emit(self, signal, *args):
|
|||||||
self.emit(signal, *args)
|
self.emit(signal, *args)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _safe_wrapper(func, *args):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
try:
|
||||||
|
func(*args)
|
||||||
|
finally:
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
def safe_idle_add(func, *args):
|
||||||
|
"""
|
||||||
|
Make sure idle functions are run thread safe
|
||||||
|
"""
|
||||||
|
return gobject.idle_add(_safe_wrapper, func, *args)
|
||||||
|
|
||||||
|
def safe_timeout_add(timeout, func, *args):
|
||||||
|
"""
|
||||||
|
Make sure timeout functions are run thread safe
|
||||||
|
"""
|
||||||
|
return gobject.timeout_add(timeout, _safe_wrapper, func, *args)
|
||||||
|
|
||||||
def uuidstr(rawuuid):
|
def uuidstr(rawuuid):
|
||||||
hx = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
|
hx = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
|
||||||
uuid = []
|
uuid = []
|
||||||
|
Loading…
Reference in New Issue
Block a user