Commit 0f9a94f2 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Split mdate(), mwait(), msleep() to POSIX and Win32 files

Also implement correct run-time POSIX clock selection.
parent 78a752b6
......@@ -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)
......
......@@ -55,68 +55,8 @@
* 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
*****************************************************************************/
......
......@@ -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);
......
......@@ -34,62 +34,16 @@
#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) */
......
......@@ -37,6 +37,7 @@
#include <unistd.h> /* fsync() */
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sched.h>
#ifdef __linux__
......@@ -66,6 +67,74 @@
# 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
{
......
......@@ -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;