]> git.sesse.net Git - vlc/commitdiff
Split mdate(), mwait(), msleep() to POSIX and Win32 files
authorRémi Denis-Courmont <remi@remlab.net>
Tue, 12 Jul 2011 15:02:54 +0000 (18:02 +0300)
committerRémi Denis-Courmont <remi@remlab.net>
Tue, 12 Jul 2011 15:14:36 +0000 (18:14 +0300)
Also implement correct run-time POSIX clock selection.

configure.ac
include/vlc_mtime.h
include/vlc_threads.h
src/misc/mtime.c
src/posix/thread.c
src/win32/thread.c

index e567c2598b45ab7ea97bcf3a38ed000479fc6f27..c5bf290deff4129757be334f5096050fdf7e0a6d 100644 (file)
@@ -566,6 +566,7 @@ dnl Check for system libs needed
 need_libc=false
 
 dnl Check for usual libc functions
+AC_CHECK_DECLS([nanosleep],,,[#include <time.h>])
 AC_CHECK_FUNCS([daemon fcntl fdopendir fstatvfs fork getenv getpwuid_r gettimeofday if_nameindex if_nametoindex isatty lstat memalign mmap openat pread posix_fadvise posix_madvise posix_memalign setlocale stricmp strnicmp uselocale])
 AC_REPLACE_FUNCS([asprintf atof atoll dirfd flockfile fsync getdelim getpid gmtime_r lldiv localtime_r nrand48 rewind setenv strcasecmp strcasestr strdup strlcpy strncasecmp strndup strnlen strsep strtof strtok_r strtoll swab tdestroy vasprintf])
 AC_CHECK_FUNCS(fdatasync,,
@@ -772,23 +773,19 @@ if test "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"; then
 
   AC_CHECK_LIB(rt, clock_nanosleep, [
     VLC_ADD_LIBS([libvlccore],[-lrt])
-    AC_DEFINE(HAVE_CLOCK_NANOSLEEP, 1, [Define to 1 if you have clock_nanosleep.])
   ], [
+    AC_CHECK_FUNC(nanosleep,,[
+      AC_CHECK_LIB(rt,nanosleep, [
+        VLC_ADD_LIBS([libvlccore],[-lrt])
+      ], [
+        AC_CHECK_LIB(posix4,nanosleep, [
+          VLC_ADD_LIBS([libvlccore],[-lposix4])
+        ])
+      ])
+    ])
     dnl HP/UX port
     AC_CHECK_LIB(rt,sem_init, [VLC_ADD_LIBS([libvlccore],[-lrt])])
   ])
-
-  have_nanosleep=false
-  AC_CHECK_FUNCS(nanosleep,have_nanosleep=:,[
-    AC_CHECK_LIB(rt,nanosleep,
-      [VLC_ADD_LIBS([libvlccore],[-lrt]) have_nanosleep=:],
-      [AC_CHECK_LIB(posix4,nanosleep,
-          [VLC_ADD_LIBS([libvlccore],[-lposix4]) have_nanosleep=:])]
-    )
-  ])
-  if ${have_nanosleep}; then
-    AC_DEFINE(HAVE_NANOSLEEP, 1, [Define if nanosleep is available.])
-  fi
 fi # end "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"
 
 dnl Check for misc headers
@@ -834,28 +831,6 @@ then
 fi
 ])
 
-dnl Mac OS X and other OSes don't have declaration for nanosleep
-if test "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"; then
-  AC_MSG_CHECKING(for nanosleep in time.h)
-  AC_EGREP_HEADER(nanosleep,time.h,[
-    AC_MSG_RESULT(yes)
-    AC_DEFINE(HAVE_DECL_NANOSLEEP, 1,
-              Define if <time.h> defines nanosleep.)
-  ],[
-    AC_MSG_RESULT(no)
-  ])
-fi # end "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"
-
-dnl Make sure we have timespecs
-AC_MSG_CHECKING(for timespec in sys/time.h)
-AC_EGREP_HEADER(timespec,sys/time.h,[
-  AC_MSG_RESULT(yes)
-  AC_DEFINE(HAVE_STRUCT_TIMESPEC, 1,
-            Define if <sys/time.h> defines struct timespec.)
-],[
-  AC_MSG_RESULT(no)
-])
-
 dnl Check for threads library
 if test "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"; then
   AC_CHECK_HEADERS(pthread.h)
index 46b666a63bfe08ae52d9249e348816cd91e83b29..0e623c2074de95daf30c59dee8b1970797d5610d 100644 (file)
  * Prototypes
  *****************************************************************************/
 VLC_API char * mstrtime( char *psz_buffer, mtime_t date );
-VLC_API mtime_t mdate( void );
-VLC_API void mwait( mtime_t date );
-VLC_API void msleep( mtime_t delay );
 VLC_API char * secstotimestr( char *psz_buffer, int32_t secs );
 
-# define VLC_HARD_MIN_SLEEP 10000   /* 10 milliseconds = 1 tick at 100Hz */
-
-#if defined (__GNUC__) \
- && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
-/* Linux has 100, 250, 300 or 1000Hz
- *
- * HZ=100 by default on FreeBSD, but some architectures use a 1000Hz timer
- */
-# define VLC_SOFT_MIN_SLEEP 9000000 /* 9 seconds */
-
-static
-__attribute__((unused))
-__attribute__((noinline))
-__attribute__((error("sorry, cannot sleep for such short a time")))
-mtime_t impossible_delay( mtime_t delay )
-{
-    (void) delay;
-    return VLC_HARD_MIN_SLEEP;
-}
-
-static
-__attribute__((unused))
-__attribute__((noinline))
-__attribute__((warning("use proper event handling instead of short delay")))
-mtime_t harmful_delay( mtime_t delay )
-{
-    return delay;
-}
-
-# define check_delay( d ) \
-    ((__builtin_constant_p(d < VLC_HARD_MIN_SLEEP) \
-   && (d < VLC_HARD_MIN_SLEEP)) \
-       ? impossible_delay(d) \
-       : ((__builtin_constant_p(d < VLC_SOFT_MIN_SLEEP) \
-       && (d < VLC_SOFT_MIN_SLEEP)) \
-           ? harmful_delay(d) \
-           : d))
-
-static
-__attribute__((unused))
-__attribute__((noinline))
-__attribute__((error("deadlines can not be constant")))
-mtime_t impossible_deadline( mtime_t deadline )
-{
-    return deadline;
-}
-
-# define check_deadline( d ) \
-    (__builtin_constant_p(d) ? impossible_deadline(d) : d)
-#else
-# define check_delay(d) (d)
-# define check_deadline(d) (d)
-#endif
-
-#define msleep(d) msleep(check_delay(d))
-#define mwait(d) mwait(check_deadline(d))
-
 /*****************************************************************************
  * date_t: date incrementation without long-term rounding errors
  *****************************************************************************/
index d052f13984303782bac70c8a9ffb9ffe33fa8915..8fa2cf0906ef20cfa621acc00fae1ceb954edd85 100644 (file)
@@ -200,6 +200,67 @@ VLC_API void vlc_cancel(vlc_thread_t);
 VLC_API void vlc_join(vlc_thread_t, void **);
 VLC_API void vlc_control_cancel (int cmd, ...);
 
+VLC_API mtime_t mdate(void);
+VLC_API void mwait(mtime_t deadline);
+VLC_API void msleep(mtime_t delay);
+
+#define VLC_HARD_MIN_SLEEP   10000 /* 10 milliseconds = 1 tick at 100Hz */
+#define VLC_SOFT_MIN_SLEEP 9000000 /* 9 seconds */
+
+#if defined (__GNUC__) \
+ && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+/* Linux has 100, 250, 300 or 1000Hz
+ *
+ * HZ=100 by default on FreeBSD, but some architectures use a 1000Hz timer
+ */
+
+static
+__attribute__((unused))
+__attribute__((noinline))
+__attribute__((error("sorry, cannot sleep for such short a time")))
+mtime_t impossible_delay( mtime_t delay )
+{
+    (void) delay;
+    return VLC_HARD_MIN_SLEEP;
+}
+
+static
+__attribute__((unused))
+__attribute__((noinline))
+__attribute__((warning("use proper event handling instead of short delay")))
+mtime_t harmful_delay( mtime_t delay )
+{
+    return delay;
+}
+
+# define check_delay( d ) \
+    ((__builtin_constant_p(d < VLC_HARD_MIN_SLEEP) \
+   && (d < VLC_HARD_MIN_SLEEP)) \
+       ? impossible_delay(d) \
+       : ((__builtin_constant_p(d < VLC_SOFT_MIN_SLEEP) \
+       && (d < VLC_SOFT_MIN_SLEEP)) \
+           ? harmful_delay(d) \
+           : d))
+
+static
+__attribute__((unused))
+__attribute__((noinline))
+__attribute__((error("deadlines can not be constant")))
+mtime_t impossible_deadline( mtime_t deadline )
+{
+    return deadline;
+}
+
+# define check_deadline( d ) \
+    (__builtin_constant_p(d) ? impossible_deadline(d) : d)
+#else
+# define check_delay(d) (d)
+# define check_deadline(d) (d)
+#endif
+
+#define msleep(d) msleep(check_delay(d))
+#define mwait(d) mwait(check_deadline(d))
+
 VLC_API int vlc_timer_create(vlc_timer_t *, void (*) (void *), void *) VLC_USED;
 VLC_API void vlc_timer_destroy(vlc_timer_t);
 VLC_API void vlc_timer_schedule(vlc_timer_t, bool, mtime_t, mtime_t);
index 5f8e7300d694b49170550fdc2f4d80da40b5ae61..fc4abdfff66adab17761cf30643a86b9379cc4ed 100644 (file)
 #endif
 
 #include <vlc_common.h>
-
-#include <time.h>                      /* clock_gettime(), clock_nanosleep() */
 #include <assert.h>
-#include <errno.h>
 
 #ifdef HAVE_UNISTD_H
-#   include <unistd.h>                                           /* select() */
-#endif
-
-#ifdef HAVE_KERNEL_OS_H
-#   include <kernel/OS.h>
-#endif
-
-#if defined( WIN32 ) || defined( UNDER_CE )
-#   include <windows.h>
-#   include <mmsystem.h>
-#endif
-
-#if defined(HAVE_SYS_TIME_H)
-#   include <sys/time.h>
-#endif
-
-#if defined(__APPLE__) && !defined(__powerpc__) && !defined(__ppc__) && !defined(__ppc64__)
-#define USE_APPLE_MACH 1
-#   include <mach/mach.h>
-#   include <mach/mach_time.h>
-#endif
-
-#if !defined(HAVE_STRUCT_TIMESPEC)
-struct timespec
-{
-    time_t  tv_sec;
-    int32_t tv_nsec;
-};
-#endif
-
-#if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP)
-int nanosleep(struct timespec *, struct timespec *);
-#endif
-
-#if !defined (_POSIX_CLOCK_SELECTION)
-#  define _POSIX_CLOCK_SELECTION (-1)
+# include <unistd.h>
 #endif
-
-# if (_POSIX_CLOCK_SELECTION < 0)
-/*
- * We cannot use the monotonic clock if clock selection is not available,
- * as it would screw vlc_cond_timedwait() completely. Instead, we have to
- * stick to the realtime clock. Nevermind it screws everything up when ntpdate
- * warps the wall clock.
- */
-#  undef CLOCK_MONOTONIC
-#  define CLOCK_MONOTONIC CLOCK_REALTIME
-#elif !defined (HAVE_CLOCK_NANOSLEEP)
-/* Clock selection without clock in the first place, I don't think so. */
-#  error We have quite a situation here! Fix me if it ever happens.
+#if !defined (_POSIX_TIMERS)
+# define _POSIX_TIMERS (-1)
+# include <sys/time.h>
+#else
+# include <time.h> /* clock_gettime() */
 #endif
 
 /**
@@ -149,211 +103,6 @@ char *secstotimestr( char *psz_buffer, int32_t i_seconds )
     return psz_buffer;
 }
 
-#if defined (HAVE_CLOCK_NANOSLEEP)
-static unsigned prec = 0;
-
-static void mprec_once( void )
-{
-    struct timespec ts;
-    if( clock_getres( CLOCK_MONOTONIC, &ts ))
-        clock_getres( CLOCK_REALTIME, &ts );
-
-    prec = ts.tv_nsec / 1000;
-}
-#endif
-
-/**
- * Return a value that is no bigger than the clock precision
- * (possibly zero).
- */
-static inline unsigned mprec( void )
-{
-#if defined (HAVE_CLOCK_NANOSLEEP)
-    static pthread_once_t once = PTHREAD_ONCE_INIT;
-    pthread_once( &once, mprec_once );
-    return prec;
-#else
-    return 0;
-#endif
-}
-
-#ifdef USE_APPLE_MACH
-static mach_timebase_info_data_t mtime_timebase_info;
-static pthread_once_t mtime_timebase_info_once = PTHREAD_ONCE_INIT;
-static void mtime_init_timebase(void)
-{
-    mach_timebase_info(&mtime_timebase_info);
-}
-#endif
-
-/**
- * Return high precision date
- *
- * Use a 1 MHz clock when possible, or 1 kHz
- *
- * Beware ! It doesn't reflect the actual date (since epoch), but can be the machine's uptime or anything (when monotonic clock is used)
- */
-mtime_t mdate( void )
-{
-    mtime_t res;
-
-#if defined (HAVE_CLOCK_NANOSLEEP)
-    struct timespec ts;
-
-    /* Try to use POSIX monotonic clock if available */
-    if( clock_gettime( CLOCK_MONOTONIC, &ts ) == EINVAL )
-        /* Run-time fallback to real-time clock (always available) */
-        (void)clock_gettime( CLOCK_REALTIME, &ts );
-
-    res = ((mtime_t)ts.tv_sec * (mtime_t)1000000)
-           + (mtime_t)(ts.tv_nsec / 1000);
-
-#elif defined( HAVE_KERNEL_OS_H )
-    res = real_time_clock_usecs();
-
-#elif defined( USE_APPLE_MACH )
-    pthread_once(&mtime_timebase_info_once, mtime_init_timebase);
-    uint64_t date = mach_absolute_time();
-    mach_timebase_info_data_t tb = mtime_timebase_info;
-
-    /* tb.denom is uint32_t, switch to 64 bits to prevent overflow. */
-    uint64_t denom = tb.denom;
-
-    /* Switch to microsecs */
-    denom *= 1000LL;
-
-    /* Split the division to prevent overflow */
-    lldiv_t d = lldiv (tb.numer, denom);
-
-    res = (d.quot * date) + ((d.rem * date) / denom);
-
-#elif defined( WIN32 ) || defined( UNDER_CE )
-    /* We don't need the real date, just the value of a high precision timer */
-    LARGE_INTEGER counter, freq;
-    if (!QueryPerformanceCounter (&counter)
-     || !QueryPerformanceFrequency (&freq))
-        abort();
-
-    /* Convert to from (1/freq) to microsecond resolution */
-    /* We need to split the division to avoid 63-bits overflow */
-    lldiv_t d = lldiv (counter.QuadPart, freq.QuadPart);
-
-    res = (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
-
-#else
-    struct timeval tv_date;
-
-    /* gettimeofday() cannot fail given &tv_date is a valid address */
-    (void)gettimeofday( &tv_date, NULL );
-    res = (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
-#endif
-
-    return res;
-}
-
-#undef mwait
-/**
- * Wait for a date
- *
- * This function uses select() and an system date function to wake up at a
- * precise date. It should be used for process synchronization. If current date
- * is posterior to wished date, the function returns immediately.
- * \param date The date to wake up at
- */
-void mwait( mtime_t date )
-{
-    /* If the deadline is already elapsed, or within the clock precision,
-     * do not even bother the system timer. */
-    date -= mprec();
-
-#if defined (HAVE_CLOCK_NANOSLEEP)
-    lldiv_t d = lldiv( date, 1000000 );
-    struct timespec ts = { d.quot, d.rem * 1000 };
-
-    int val;
-    while( ( val = clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts,
-                                    NULL ) ) == EINTR );
-    if( val == EINVAL )
-    {
-        ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000;
-        while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL ) == EINTR );
-    }
-
-#elif defined (WIN32)
-    mtime_t i_total;
-
-    while( (i_total = (date - mdate())) > 0 )
-    {
-        const mtime_t i_sleep = i_total / 1000;
-        DWORD i_delay = (i_sleep > 0x7fffffff) ? 0x7fffffff : i_sleep;
-        vlc_testcancel();
-        SleepEx( i_delay, TRUE );
-    }
-    vlc_testcancel();
-
-#else
-    mtime_t delay = date - mdate();
-    if( delay > 0 )
-        msleep( delay );
-
-#endif
-}
-
-
-#include "libvlc.h" /* vlc_backtrace() */
-#undef msleep
-
-/**
- * Portable usleep(). Cancellation point.
- *
- * \param delay the amount of time to sleep
- */
-void msleep( mtime_t delay )
-{
-#if defined( HAVE_CLOCK_NANOSLEEP )
-    lldiv_t d = lldiv( delay, 1000000 );
-    struct timespec ts = { d.quot, d.rem * 1000 };
-
-    int val;
-    while( ( val = clock_nanosleep( CLOCK_MONOTONIC, 0, &ts, &ts ) ) == EINTR );
-    if( val == EINVAL )
-    {
-        ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000;
-        while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, &ts ) == EINTR );
-    }
-
-#elif defined( HAVE_KERNEL_OS_H )
-    snooze( delay );
-
-#elif defined( WIN32 ) || defined( UNDER_CE )
-    mwait (mdate () + delay);
-
-#elif defined( HAVE_NANOSLEEP )
-    struct timespec ts_delay;
-
-    ts_delay.tv_sec = delay / 1000000;
-    ts_delay.tv_nsec = (delay % 1000000) * 1000;
-
-    while( nanosleep( &ts_delay, &ts_delay ) && ( errno == EINTR ) );
-
-#elif defined (USE_APPLE_MACH)
-    /* The version that should be used, if it was cancelable */
-    pthread_once(&mtime_timebase_info_once, mtime_init_timebase);
-    uint64_t mach_time = delay * 1000 * mtime_timebase_info.denom / mtime_timebase_info.numer;
-    mach_wait_until(mach_time + mach_absolute_time());
-
-#else
-    struct timeval tv_delay;
-
-    tv_delay.tv_sec = delay / 1000000;
-    tv_delay.tv_usec = delay % 1000000;
-
-    /* If a signal is caught, you are screwed. Update your OS to nanosleep()
-     * or clock_nanosleep() if this is an issue. */
-    select( 0, NULL, NULL, NULL, &tv_delay );
-#endif
-}
-
 /*
  * Date management (internal and external)
  */
@@ -528,16 +277,21 @@ static int gettimeofday (struct timeval *tv, void *tz )
  */
 uint64_t NTPtime64 (void)
 {
+#if (_POSIX_TIMERS > 0)
     struct timespec ts;
-#if defined (CLOCK_REALTIME) && !defined (__OS2__)
+
     clock_gettime (CLOCK_REALTIME, &ts);
 #else
+    struct timeval tv;
+    struct
     {
-        struct timeval tv;
-        gettimeofday (&tv, NULL);
-        ts.tv_sec = tv.tv_sec;
-        ts.tv_nsec = tv.tv_usec * 1000;
-    }
+        uint32_t tv_sec;
+        uint32_t tv_nsec;
+    } ts;
+
+    gettimeofday (&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
 #endif
 
     /* Convert nanoseconds to 32-bits fraction (232 picosecond units) */
index 024d83b6fcd19f0f53bdb0e604d1144ae8f7fc5e..eed068768493e22311ea1ce1e902e00e7651770a 100644 (file)
@@ -37,6 +37,7 @@
 #include <unistd.h> /* fsync() */
 #include <signal.h>
 #include <errno.h>
+#include <time.h>
 
 #include <sched.h>
 #ifdef __linux__
 # include <sys/pset.h>
 #endif
 
+#if !defined (_POSIX_TIMERS)
+# define _POSIX_TIMERS (-1)
+#endif
+#if !defined (_POSIX_CLOCK_SELECTION)
+/* Clock selection was defined in 2001 and became mandatory in 2008. */
+# define _POSIX_CLOCK_SELECTION (-1)
+#endif
+#if !defined (_POSIX_MONOTONIC_CLOCK)
+# define _POSIX_MONOTONIC_CLOCK (-1)
+#endif
+
+#if (_POSIX_TIMERS > 0)
+static unsigned vlc_clock_prec;
+
+# if (_POSIX_CLOCK_SELECTION > 0)
+/* POSIX clock selection is needed so that vlc_cond_timewait() is consistent
+ * with mdate() and mwait(). Otherwise, the monotonic clock cannot be used.
+ * Fortunately, clock selection has become mandatory as of 2008 so that really
+ * only broken old systems still lack it. */
+
+#  if (_POSIX_MONOTONIC_CLOCK > 0)
+/* Compile-time POSIX monotonic clock support */
+#   define vlc_clock_id (CLOCK_MONOTONIC)
+
+#  elif (_POSIX_MONOTONIC_CLOCK == 0)
+/* Run-time POSIX monotonic clock support (see clock_setup() below) */
+static clockid_t vlc_clock_id;
+
+#  else
+/* No POSIX monotonic clock support */
+#   define vlc_clock_id (CLOCK_REALTIME)
+#   warning Monotonic clock not available. Expect timing issues.
+
+#  endif /* _POSIX_MONOTONIC_CLOKC */
+# else
+/* No POSIX clock selection. */
+#  define pthread_condattr_setclock(attr, clock) (attr, clock, 0)
+#  warning Clock selection not available. Expect timing issues.
+
+# endif /* _POSIX_CLOCK_SELECTION */
+
+static void vlc_clock_setup_once (void)
+{
+# if (_POSIX_MONOTONIC_CLOCK == 0)
+    long val = sysconf (_SC_MONOTONIC_CLOCK);
+    assert (val != 0);
+    vlc_clock_id = (val < 0) ? CLOCK_REALTIME : CLOCK_MONOTONIC;
+# endif
+
+    struct timespec res;
+    if (unlikely(clock_getres (vlc_clock_id, &res) != 0 || res.tv_sec != 0))
+        abort ();
+    vlc_clock_prec = (res.tv_nsec + 500) / 1000;
+}
+
+static pthread_once_t vlc_clock_once = PTHREAD_ONCE_INIT;
+
+# define vlc_clock_setup() \
+    pthread_once(&vlc_clock_once, vlc_clock_setup_once)
+
+#else /* _POSIX_TIMERS */
+
+# include <sys/time.h> /* gettimeofday() */
+# if defined (HAVE_DECL_NANOSLEEP) && !HAVE_DECL_NANOSLEEP
+int nanosleep (struct timespec *, struct timespec *);
+# endif
+
+#endif /* _POSIX_TIMERS */
 
 /**
  * Print a backtrace to the standard error for debugging purpose.
@@ -280,16 +349,10 @@ void vlc_cond_init (vlc_cond_t *p_condvar)
 {
     pthread_condattr_t attr;
 
+    vlc_clock_setup ();
     if (unlikely(pthread_condattr_init (&attr)))
         abort ();
-#if !defined (_POSIX_CLOCK_SELECTION)
-   /* Fairly outdated POSIX support (that was defined in 2001) */
-# define _POSIX_CLOCK_SELECTION (-1)
-#endif
-#if (_POSIX_CLOCK_SELECTION >= 0)
-    /* NOTE: This must be the same clock as the one in mtime.c */
-    pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
-#endif
+    pthread_condattr_setclock (&attr, vlc_clock_id);
 
     if (unlikely(pthread_cond_init (p_condvar, &attr)))
         abort ();
@@ -863,6 +926,89 @@ void vlc_control_cancel (int cmd, ...)
     assert (0);
 }
 
+/**
+ * Precision monotonic clock.
+ *
+ * In principles, the clock has a precision of 1 MHz. But the actual resolution
+ * may be much lower, especially when it comes to sleeping with mwait() or
+ * msleep(). Most general-purpose operating systems provide a resolution of
+ * only 100 to 1000 Hz.
+ *
+ * @warning The origin date (time value "zero") is not specified. It is
+ * typically the time the kernel started, but this is platform-dependent.
+ * If you need wall clock time, use gettimeofday() instead.
+ *
+ * @return a timestamp in microseconds.
+ */
+mtime_t mdate (void)
+{
+#if (_POSIX_TIMERS > 0)
+    struct timespec ts;
+
+    vlc_clock_setup ();
+    if (unlikely(clock_gettime (vlc_clock_id, &ts) != 0))
+        abort ();
+
+    return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
+
+#else
+    struct timeval tv;
+
+    if (unlikely(gettimeofday (&tv, NULL) != 0))
+        abort ();
+    return (INT64_C(1000000) * tv.tv_sec) + tv.tv_nsec;
+
+#endif
+}
+
+#undef mwait
+/**
+ * Waits until a deadline (possibly later due to OS scheduling).
+ * @param deadline timestamp to wait for (see mdate())
+ */
+void mwait (mtime_t deadline)
+{
+#if (_POSIX_TIMERS > 0)
+    vlc_clock_setup ();
+    /* If the deadline is already elapsed, or within the clock precision,
+     * do not even bother the system timer. */
+    deadline -= vlc_clock_prec;
+
+    lldiv_t d = lldiv (deadline, 1000000);
+    struct timespec ts = { d.quot, d.rem * 1000 };
+
+    while (clock_nanosleep (vlc_clock_id, TIMER_ABSTIME, &ts, NULL) == EINTR);
+
+#else
+    deadline -= mdate ();
+    if (deadline > 0)
+        msleep (deadline);
+
+#endif
+}
+
+#undef msleep
+/**
+ * Waits for an interval of time.
+ * @param delay how long to wait (in microseconds)
+ */
+void msleep (mtime_t delay)
+{
+    vlc_clock_setup ();
+
+    lldiv_t d = lldiv (delay, 1000000);
+    struct timespec ts = { d.quot, d.rem * 1000 };
+
+#if (_POSIX_TIMERS > 0)
+    while (clock_nanosleep (vlc_clock_id, 0, &ts, &ts) == EINTR);
+
+#else
+    while (nanosleep (&ts, &ts) == -1)
+        assert (errno == EINTR);
+
+#endif
+}
+
 
 struct vlc_timer
 {
index 9b83f84cc674fdc754768ceaf0b66dd1277949d4..360a411f3c46567092356f8a1018e27bdf849b89 100644 (file)
@@ -778,6 +778,45 @@ void vlc_control_cancel (int cmd, ...)
 }
 
 
+/*** Clock ***/
+mtime_t mdate (void)
+{
+    /* We don't need the real date, just the value of a high precision timer */
+    LARGE_INTEGER counter, freq;
+    if (!QueryPerformanceCounter (&counter)
+     || !QueryPerformanceFrequency (&freq))
+        abort();
+
+    /* Convert to from (1/freq) to microsecond resolution */
+    /* We need to split the division to avoid 63-bits overflow */
+    lldiv_t d = lldiv (counter.QuadPart, freq.QuadPart);
+
+    return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
+}
+
+#undef mwait
+void mwait (mtime_t deadline)
+{
+    mtime_t delay;
+
+    vlc_testcancel();
+    while ((delay = (deadline - mdate())) > 0)
+    {
+        delay /= 1000;
+        if (unlikely(delay > 0x7fffffff))
+            delay = 0x7fffffff;
+        SleepEx (delay, TRUE);
+        vlc_testcancel();
+    }
+}
+
+#undef msleep
+void msleep (mtime_t delay)
+{
+    mwait (mdate () + delay);
+}
+
+
 /*** Timers ***/
 struct vlc_timer
 {