[KVM-AUTOTEST PATCH 01/28] KVM test: introduce a helper class to run a function in the background

Michael Goldish
2010-12-27T16:01:28Z

This class allows running a function in a background thread. If the function
raises an exception, the exception is recorded and re-raised when join() is
called. Otherwise, join() returns the value returned by the function.

This is useful for performing multiple simple actions in parallel (such as
rebooting several VMs at once to save time). It may also be useful for running
tests on a VM while migrating it (but then it wouldn't be safe to pass the VM
object as a parameter to this class).

Differences from Jason Wang's original patch:
- Inherit from threading.Thread instead of wrapping it.
- Record the returned value as well as the exception raised.
- When re-raising the exception, use its original traceback so the user will
know where the original exception was raised.
- Destroy all potentially circular references (self._args and the traceback in
self._e can refer to the thread object itself).
- Rename BackgroundTest to Thread.

Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
client/tests/kvm/kvm_utils.py | 60 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 59 insertions(+), 1 deletions(-)

diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index 5496e06..5d79112 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -5,7 +5,7 @@ KVM test utility functions.
"""

import time, string, random, socket, os, signal, re, logging, commands, cPickle
-import fcntl, shelve, ConfigParser, rss_file_transfer
+import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys
from autotest_lib.client.bin import utils, os_dep
from autotest_lib.client.common_lib import error, logging_config
import kvm_subprocess
@@ -1044,6 +1044,64 @@ def get_vendor_from_pci_id(pci_id):
return re.sub(":", " ", commands.getoutput(cmd))

+class Thread(threading.Thread):
+ """
+ Runs a test in the background thread.
+ """
+ def __init__(self, target, args=(), kwargs={}):
+ """
+ Initialize the instance.
+
+ @param target: Function to run in the thread.
+ @param args: Arguments to pass to target.
+ @param kwargs: Keyword arguments to pass to target.
+ """
+ threading.Thread.__init__(self)
+ self._target = target
+ self._args = args
+ self._kwargs = kwargs
+
+
+ def run(self):
+ """
+ Run target (passed to the constructor). No point in calling this
+ function directly. Call start() to make this function run in a new
+ thread.
+ """
+ self._e = None
+ self._retval = None
+ try:
+ try:
+ self._retval = self._target(*self._args, **self._kwargs)
+ except:
+ self._e = sys.exc_info()
+ raise
+ finally:
+ # Avoid circular references (start() may be called only once so
+ # it's OK to delete these)
+ del self._target, self._args, self._kwargs
+
+
+ def join(self, timeout=None):
+ """
+ Join the thread. If target raised an exception, re-raise it.
+ Otherwise, return the value returned by target.
+
+ @param timeout: Timeout value to pass to threading.Thread.join().
+ """
+ threading.Thread.join(self, timeout)
+ try:
+ if self._e:
+ raise self._e[0], self._e[1], self._e[2]
+ else:
+ return self._retval
+ finally:
+ # Avoid circular references (join() may be called multiple times
+ # so we can't delete these)
+ self._e = None
+ self._retval = None
+
+
class KvmLoggingConfig(logging_config.LoggingConfig):
"""
Used with the sole purpose of providing convenient logging setup
--
1.7.3.3