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:
parent
3ff34c79e2
commit
ce74cd7726
|
@ -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 = []
|
||||||
|
|
Loading…
Reference in New Issue