connection: Protect conn state tick() updates with a lock

If the mainloop is backed up, tick_send_signals might not run before
another conn.tick call is scheduled by the tick thread. conn.tick would
then be operating on incorrect self._vms since it was never updated.

Don't run another tick() until tick_send_signals has released a lock.

https://www.redhat.com/archives/virt-tools-list/2015-April/msg00009.html

Reported-by: Charles Arnold <carnold@suse.com>
This commit is contained in:
Cole Robinson 2015-04-01 19:10:16 -04:00
parent 3ff34c79e2
commit ce74cd7726
1 changed files with 45 additions and 27 deletions

View File

@ -23,13 +23,14 @@ from gi.repository import GObject
import logging import logging
import os import os
import socket import socket
import threading
import time import time
import traceback import traceback
from virtinst import support
import libvirt import libvirt
import virtinst import virtinst
from virtinst import pollhelpers from virtinst import pollhelpers
from virtinst import support
from virtinst import util from virtinst import util
from . import connectauth from . import connectauth
@ -119,6 +120,8 @@ class vmmConnection(vmmGObject):
self.record = [] self.record = []
self.hostinfo = None self.hostinfo = None
self._tick_lock = threading.Lock()
self.mediadev_initialized = False self.mediadev_initialized = False
self.mediadev_error = "" self.mediadev_error = ""
self.mediadev_use_libvirt = False self.mediadev_use_libvirt = False
@ -1144,21 +1147,31 @@ class vmmConnection(vmmGObject):
self.hostinfo = self._backend.getInfo() self.hostinfo = self._backend.getInfo()
(goneNets, newNets, nets) = self._update_nets(pollnet) try:
self._refresh_new_objects(newNets.values()) # We take the ticklock before using the conn object lists
(gonePools, newPools, pools) = self._update_pools(pollpool) # like self._vms. This ensures that those lists were updated
self._refresh_new_objects(newPools.values()) # by tick_send_signals since the last time we ran tick()
(goneInterfaces, # https://www.redhat.com/archives/virt-tools-list/2015-April/msg00009.html
newInterfaces, interfaces) = self._update_interfaces(polliface) self._tick_lock.acquire()
self._refresh_new_objects(newInterfaces.values())
# Refreshing these is handled by the mediadev callback (goneNets, newNets, nets) = self._update_nets(pollnet)
(goneNodedevs, self._refresh_new_objects(newNets.values())
newNodedevs, nodedevs) = self._update_nodedevs(pollnodedev) (gonePools, newPools, pools) = self._update_pools(pollpool)
self._refresh_new_objects(newPools.values())
(goneInterfaces,
newInterfaces, interfaces) = self._update_interfaces(polliface)
self._refresh_new_objects(newInterfaces.values())
# These are refreshing in their __init__ method, because the # Refreshing these is handled by the mediadev callback
# data is wanted immediately (goneNodedevs,
(goneVMs, newVMs, vms) = self._update_vms(pollvm) newNodedevs, nodedevs) = self._update_nodedevs(pollnodedev)
# These are refreshing in their __init__ method, because the
# data is wanted immediately
(goneVMs, newVMs, vms) = self._update_vms(pollvm)
except:
self._tick_lock.release()
raise
def tick_send_signals(): def tick_send_signals():
""" """
@ -1166,20 +1179,23 @@ class vmmConnection(vmmGObject):
updates need to go here to enable threading that doesn't block the updates need to go here to enable threading that doesn't block the
app with long tick operations. app with long tick operations.
""" """
# Connection closed out from under us try:
if not self._backend.is_open(): # Connection closed out from under us
return if not self._backend.is_open():
return
if pollvm: if pollvm:
self._vms = vms self._vms = vms
if pollnet: if pollnet:
self._nets = nets self._nets = nets
if polliface: if polliface:
self._interfaces = interfaces self._interfaces = interfaces
if pollpool: if pollpool:
self._pools = pools self._pools = pools
if pollnodedev: if pollnodedev:
self._nodedevs = nodedevs self._nodedevs = nodedevs
finally:
self._tick_lock.release()
if not self.mediadev_initialized: if not self.mediadev_initialized:
self._init_mediadev() self._init_mediadev()
@ -1239,6 +1255,8 @@ class vmmConnection(vmmGObject):
if finish_connecting: if finish_connecting:
self._change_state(self._STATE_ACTIVE) self._change_state(self._STATE_ACTIVE)
# Anything that could possibly fail before this call needs to go
# in the try/except that handles the tick lock
self.idle_add(tick_send_signals) self.idle_add(tick_send_signals)
ticklist = [] ticklist = []