Enable "wait_until_true" when used ouf the main thread 15/60215/2
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Thu, 26 Jul 2018 14:21:45 +0000 (15:21 +0100)
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Thu, 26 Jul 2018 14:32:46 +0000 (15:32 +0100)
"util.wait_until_true" uses "util.Timer" to create an active wait for a
condition. "Timer" class uses "signal" to create a watchdog to track the
time lapsed.

When used out of the main thread, "Timer" raises the following error:
  ValueError: signal only works in main thread

To make "util.wait_until_true" usable always, a new waitting method is
implemented.

JIRA: YARDSTICK-1358

Change-Id: Ifb5ba0b17b5beca0af5ceab4f6431d58b7928762
Signed-off-by: Rodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
yardstick/common/utils.py
yardstick/tests/unit/common/test_utils.py

index 83ddbd4..c74dd67 100644 (file)
@@ -28,6 +28,7 @@ import socket
 import subprocess
 import sys
 import time
+import threading
 
 import six
 from flask import jsonify
@@ -475,6 +476,9 @@ class Timer(object):
     def __del__(self):  # pragma: no cover
         signal.alarm(0)
 
+    def delta_time_sec(self):
+        return (datetime.datetime.now() - self.start).total_seconds()
+
 
 def read_meminfo(ssh_client):
     """Read "/proc/meminfo" file and parse all keys and values"""
@@ -521,17 +525,30 @@ def open_relative_file(path, task_path):
 def wait_until_true(predicate, timeout=60, sleep=1, exception=None):
     """Wait until callable predicate is evaluated as True
 
+    When in a thread different from the main one, Timer(timeout) will fail
+    because signal is not handled. In this case
+
     :param predicate: (func) callable deciding whether waiting should continue
     :param timeout: (int) timeout in seconds how long should function wait
     :param sleep: (int) polling interval for results in seconds
     :param exception: exception instance to raise on timeout. If None is passed
                       (default) then WaitTimeout exception is raised.
     """
-    try:
-        with Timer(timeout=timeout):
-            while not predicate():
+    if isinstance(threading.current_thread(), threading._MainThread):
+        try:
+            with Timer(timeout=timeout):
+                while not predicate():
+                    time.sleep(sleep)
+        except exceptions.TimerTimeout:
+            if exception and issubclass(exception, Exception):
+                raise exception  # pylint: disable=raising-bad-type
+            raise exceptions.WaitTimeout
+    else:
+        with Timer() as timer:
+            while timer.delta_time_sec() < timeout:
+                if predicate():
+                    return
                 time.sleep(sleep)
-    except exceptions.TimerTimeout:
         if exception and issubclass(exception, Exception):
             raise exception  # pylint: disable=raising-bad-type
         raise exceptions.WaitTimeout
index bf0b518..7c58a82 100644 (file)
@@ -12,12 +12,14 @@ import errno
 import importlib
 import ipaddress
 from itertools import product, chain
-import mock
 import os
-import six
-from six.moves import configparser
 import socket
 import time
+import threading
+
+import mock
+import six
+from six.moves import configparser
 import unittest
 
 import yardstick
@@ -1277,6 +1279,10 @@ class TimerTestCase(ut_base.BaseUnitTestCase):
             time.sleep(1.1)
         self.assertEqual(2, len(iterations))
 
+    def test_delta_time_sec(self):
+        with utils.Timer() as timer:
+            self.assertIsInstance(timer.delta_time_sec(), float)
+
 
 class WaitUntilTrueTestCase(ut_base.BaseUnitTestCase):
 
@@ -1298,6 +1304,15 @@ class WaitUntilTrueTestCase(ut_base.BaseUnitTestCase):
                 utils.wait_until_true(lambda: False, timeout=1, sleep=1,
                                       exception=MyTimeoutException))
 
+    def _run_thread(self):
+        with self.assertRaises(exceptions.WaitTimeout):
+            utils.wait_until_true(lambda: False, timeout=1, sleep=1)
+
+    def test_timeout_no_main_thread(self):
+        new_thread = threading.Thread(target=self._run_thread)
+        new_thread.start()
+        new_thread.join(timeout=3)
+
 
 class SendSocketCommandTestCase(unittest.TestCase):