Linux: more flexibility with monotonic clock
authorDaniel Drake <dsd@gentoo.org>
Fri, 11 Sep 2009 21:09:12 +0000 (22:09 +0100)
committerDaniel Drake <dsd@gentoo.org>
Fri, 11 Sep 2009 21:12:27 +0000 (22:12 +0100)
Some users have reported that CLOCK_MONOTONIC does not work on their
systems - I suspect it is available on x86 but perhaps not some
of the more uncommon architectures. We should fall back on
CLOCK_REALTIME in these cases.

Also, CLOCK_MONOTONIC_RAW seems even more monotonic, so we should use
that if it is available.

We now test different clock IDs during initialization to find the
best one that works.

libusb/os/linux_usbfs.c

index 1280188..bffd780 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux usbfs backend for libusb
- * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org>
  * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
  *
  * This library is free software; you can redistribute it and/or
 
 static const char *usbfs_path = NULL;
 
+/* clock ID for monotonic clock, as not all clock sources are available on all
+ * systems. appropriate choice made at initialization time. */
+static clockid_t monotonic_clkid = -1;
+
 /* do we have a busnum to relate devices? this also implies that we can read
  * the active configuration through bConfigurationValue */
 static int sysfs_can_relate_devices = -1;
@@ -172,6 +176,38 @@ static const char *find_usbfs_path(void)
        return ret;
 }
 
+static clockid_t find_monotonic_clock(void)
+{
+       struct timespec ts;
+       int i;
+       const clockid_t clktypes[] = {
+               /* the most monotonic clock I know about, but only available since
+                * Linux 2.6.28, and not even available in glibc-2.10 */
+#ifndef CLOCK_MONOTONIC_RAW
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+               CLOCK_MONOTONIC_RAW,
+
+               /* a monotonic clock, but it's not available on all architectures,
+                * and is susceptible to ntp adjustments */
+               CLOCK_MONOTONIC,
+
+               /* the fallback option */
+               CLOCK_REALTIME,
+       };
+
+       for (i = 0; i < (sizeof(clktypes) / sizeof(*clktypes)); i++) {
+               int r = clock_gettime(clktypes[i], &ts);
+               if (r == 0) {
+                       usbi_dbg("clock %d selected", clktypes[i]);
+                       return r;
+               }
+               usbi_dbg("clock %d doesn't work", clktypes[i]);
+       }
+
+       return -1;
+}
+
 static int op_init(struct libusb_context *ctx)
 {
        struct stat statbuf;
@@ -183,6 +219,14 @@ static int op_init(struct libusb_context *ctx)
                return LIBUSB_ERROR_OTHER;
        }
 
+       if (monotonic_clkid == -1) {
+               monotonic_clkid = find_monotonic_clock();
+               if (monotonic_clkid == -1) {
+                       usbi_err(ctx, "could not find working monotonic clock");
+                       return LIBUSB_ERROR_OTHER;
+               }
+       }
+
        r = stat(SYSFS_DEVICE_PATH, &statbuf);
        if (r == 0 && S_ISDIR(statbuf.st_mode)) {
                usbi_dbg("found usb devices in sysfs");
@@ -2036,7 +2080,7 @@ static int op_clock_gettime(int clk_id, struct timespec *tp)
 {
        switch (clk_id) {
        case USBI_CLOCK_MONOTONIC:
-               return clock_gettime(CLOCK_MONOTONIC, tp);
+               return clock_gettime(monotonic_clkid, tp);
        case USBI_CLOCK_REALTIME:
                return clock_gettime(CLOCK_REALTIME, tp);
        default: