1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-03 05:18:29 +03:00
lvm2/daemons/lvmdbusd/job.py
Tony Asleson ee0c9e7b23 lvmdbusd: Take out background thread
There is no reason to create another background task when the task that
created it is going to block waiting for it to finish.  Instead we will
just execute the logic in the worker thread that is servicing the worker
queue.
2016-11-03 18:29:06 -05:00

222 lines
5.5 KiB
Python

# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .automatedproperties import AutomatedProperties
from .utils import job_obj_path_generate, mt_async_result, log_debug
from . import cfg
from .cfg import JOB_INTERFACE
import dbus
import threading
from gi.repository import GLib
# Class that handles a client waiting for something to be complete. We either
# get a timeout or the operation is done.
class WaitingClient(object):
# A timeout occurred
@staticmethod
def _timeout(wc):
with wc.rlock:
if wc.in_use:
wc.in_use = False
# Remove ourselves from waiting client
wc.job_state.remove_waiting_client(wc)
wc.timer_id = -1
mt_async_result(wc.cb, wc.job_state.Complete)
wc.job_state = None
def __init__(self, job_state, tmo, cb, cbe):
self.rlock = threading.RLock()
self.job_state = job_state
self.cb = cb
self.cbe = cbe
self.in_use = True # Indicates if object is in play
self.timer_id = -1
if tmo > 0:
self.timer_id = GLib.timeout_add_seconds(
tmo, WaitingClient._timeout, self)
# The job finished before the timer popped and we are being notified that
# it's done
def notify(self):
with self.rlock:
if self.in_use:
self.in_use = False
# Clear timer
if self.timer_id != -1:
GLib.source_remove(self.timer_id)
self.timer_id = -1
mt_async_result(self.cb, self.job_state.Complete)
self.job_state = None
# noinspection PyPep8Naming
class JobState(object):
def __init__(self, request=None):
self.rlock = threading.RLock()
self._percent = 0
self._complete = False
self._request = request
self._ec = 0
self._stderr = ''
self._waiting_clients = []
# This is an lvm command that is just taking too long and doesn't
# support background operation
if self._request:
# Faking the percentage when we don't have one
self._percent = 1
@property
def Percent(self):
with self.rlock:
return self._percent
@Percent.setter
def Percent(self, value):
with self.rlock:
self._percent = value
@property
def Complete(self):
with self.rlock:
if self._request:
self._complete = self._request.is_done()
return self._complete
@Complete.setter
def Complete(self, value):
with self.rlock:
self._complete = value
self._percent = 100
self.notify_waiting_clients()
@property
def GetError(self):
with self.rlock:
if self.Complete:
if self._request:
(rc, error) = self._request.get_errors()
return (rc, str(error))
else:
return (self._ec, self._stderr)
else:
return (-1, 'Job is not complete!')
def dtor(self):
with self.rlock:
self._request = None
@property
def Result(self):
with self.rlock:
if self._request:
return self._request.result()
return '/'
def add_waiting_client(self, client):
with self.rlock:
# Avoid race condition where it goes complete before we get added
# to the list of waiting clients
if self.Complete:
client.notify()
else:
self._waiting_clients.append(client)
def remove_waiting_client(self, client):
# If a waiting client timer pops before the job is done we will allow
# the client to remove themselves from the list. As we have a lock
# here and a lock in the waiting client too, and they can be obtained
# in different orders, a dead lock can occur.
# As this remove is really optional, we will try to acquire the lock
# and remove. If we are unsuccessful it's not fatal, we just delay
# the time when the objects can be garbage collected by python
if self.rlock.acquire(False):
try:
self._waiting_clients.remove(client)
finally:
self.rlock.release()
def notify_waiting_clients(self):
with self.rlock:
for c in self._waiting_clients:
c.notify()
self._waiting_clients = []
# noinspection PyPep8Naming
class Job(AutomatedProperties):
_Percent_meta = ('d', JOB_INTERFACE)
_Complete_meta = ('b', JOB_INTERFACE)
_Result_meta = ('o', JOB_INTERFACE)
_GetError_meta = ('(is)', JOB_INTERFACE)
def __init__(self, request, job_state=None):
super(Job, self).__init__(job_obj_path_generate())
self.set_interface(JOB_INTERFACE)
if job_state:
self.state = job_state
else:
self.state = JobState(request)
@property
def Percent(self):
return dbus.Double(float(self.state.Percent))
@property
def Complete(self):
return dbus.Boolean(self.state.Complete)
@Complete.setter
def Complete(self, value):
self.state.Complete = value
@property
def GetError(self):
return dbus.Struct(self.state.GetError, signature="(is)")
@dbus.service.method(dbus_interface=JOB_INTERFACE)
def Remove(self):
if self.state.Complete:
cfg.om.remove_object(self, True)
self.state.dtor()
else:
raise dbus.exceptions.DBusException(
JOB_INTERFACE, 'Job is not complete!')
@dbus.service.method(dbus_interface=JOB_INTERFACE,
in_signature='i',
out_signature='b',
async_callbacks=('cb', 'cbe'))
def Wait(self, timeout, cb, cbe):
if timeout == 0 or self.state.Complete:
cb(dbus.Boolean(self.state.Complete))
else:
self.state.add_waiting_client(
WaitingClient(self.state, timeout, cb, cbe))
@property
def Result(self):
return dbus.ObjectPath(self.state.Result)
@property
def lvm_id(self):
return str(id(self))
@property
def Uuid(self):
import uuid
return uuid.uuid1()