timeval: Use monotonic time in OVS Python timeval library.
authorRyan Wilson <wryan@nicira.com>
Fri, 30 May 2014 22:38:51 +0000 (15:38 -0700)
committerEthan Jackson <ethan@nicira.com>
Fri, 30 May 2014 22:43:43 +0000 (15:43 -0700)
Python's time.time() function uses the system wall clock. However,
if NTP resets the wall clock to be a time in the past, then this
causes any applications that poll block based on time, such as
ovs-xapi-sync, to poll block indefinitely since the time is
unexpectedly negative.

This patch fixes the issue by using time.monotonic() if Python's
version >= 3.3. Otherwise, the timeval module calls out to the
librt C shared library and uses the clock_gettime function with
CLOCK_MONOTONIC.

Note this is only enabled on Linux-based platforms. This has been
tested on Ubuntu 12.04 and Redhat 6.4.

Bug #1189434
Signed-off-by: Ryan Wilson <wryan@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
python/ovs/timeval.py

index ba0e54e..f2681ac 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import ctypes
+import sys
 import time
 
+LIBRT = 'librt.so.1'
+CLOCK_MONOTONIC = 1
+
+class timespec(ctypes.Structure):
+    _fields_ = [
+        ('tv_sec', ctypes.c_long),
+        ('tv_nsec', ctypes.c_long),
+    ]
+
+try:
+    librt = ctypes.CDLL(LIBRT)
+    clock_gettime = librt.clock_gettime
+    clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
+except:
+    # Librt shared library could not be loaded
+    librt = None
+
+def monotonic():
+    if not librt:
+        return time.time()
+
+    t = timespec()
+    if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) == 0:
+        return t.tv_sec + t.tv_nsec * 1e-9
+    # Kernel does not support CLOCK_MONOTONIC
+    return time.time()
+
+# Use time.monotonic() if Python version >= 3.3
+if not hasattr(time, 'monotonic'):
+    time.monotonic = monotonic
 
 def msec():
     """Returns the current time, as the amount of time since the epoch, in
     milliseconds, as a float."""
-    return time.time() * 1000.0
+    return time.monotonic() * 1000.0
 
 
 def postfork():