X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fmtime.c;h=dd525e74bb70d083f9a72574b38125b8c60c38b7;hb=3ea2708daa0caa8419e9a3d1bcd967d09f445ab8;hp=b7ec5298564a4262d8649c93f5ba5aaf80ba5fb8;hpb=85b29bdc288a1573d43bd524908be5748a9b3640;p=vlc diff --git a/src/misc/mtime.c b/src/misc/mtime.c index b7ec529856..dd525e74bb 100644 --- a/src/misc/mtime.c +++ b/src/misc/mtime.c @@ -1,11 +1,14 @@ /***************************************************************************** * mtime.c: high resolution time management functions - * Functions are prototyped in mtime.h. + * Functions are prototyped in vlc_mtime.h. ***************************************************************************** - * Copyright (C) 1998-2004 VideoLAN (Centrale Réseaux) and its contributors + * Copyright (C) 1998-2007 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont * $Id$ * * Authors: Vincent Seguin + * Rémi Denis-Courmont + * Gisle Vanem * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,21 +22,22 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -/* - * TODO: - * see if using Linux real-time extensions is possible and profitable - */ - /***************************************************************************** * Preamble *****************************************************************************/ -#include /* sprintf() */ #include +#include /* sprintf() */ +#include /* clock_gettime(), clock_nanosleep() */ +#include /* lldiv() */ +#include +#include + + #if defined( PTH_INIT_IN_PTH_H ) /* GNU Pth */ # include #endif @@ -48,11 +52,12 @@ #if defined( WIN32 ) || defined( UNDER_CE ) # include -#else +#endif +#if defined(HAVE_SYS_TIME_H) # include #endif -#if defined(HAVE_NANOSLEEP) && !defined(HAVE_STRUCT_TIMESPEC) +#if !defined(HAVE_STRUCT_TIMESPEC) struct timespec { time_t tv_sec; @@ -106,6 +111,30 @@ char *secstotimestr( char *psz_buffer, int i_seconds ) return( psz_buffer ); } +/** + * Return a value that is no bigger than the clock precision + * (possibly zero). + */ +static inline unsigned mprec( void ) +{ +#if defined (HAVE_CLOCK_NANOSLEEP) + struct timespec ts; + if( clock_getres( CLOCK_MONOTONIC, &ts )) + clock_getres( CLOCK_REALTIME, &ts ); + + return ts.tv_nsec / 1000; +#endif + return 0; +} + +static unsigned prec = 0; +static volatile mtime_t cached_time = 0; +#if defined( HAVE_CLOCK_NANOSLEEP ) +# if (_POSIX_MONOTONIC_CLOCK - 0 < 0) +# define CLOCK_MONOTONIC CLOCK_REALTIME +# endif +#endif + /** * Return high precision date * @@ -114,13 +143,25 @@ char *secstotimestr( char *psz_buffer, int i_seconds ) */ mtime_t mdate( void ) { -#if defined( HAVE_KERNEL_OS_H ) - return( real_time_clock_usecs() ); + 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( WIN32 ) || defined( UNDER_CE ) /* We don't need the real date, just the value of a high precision timer */ static mtime_t freq = I64C(-1); - mtime_t usec_time; if( freq == I64C(-1) ) { @@ -142,17 +183,23 @@ mtime_t mdate( void ) * the RTC rather than the TSC. If it's anything else, we * presume that the performance counter is unreliable. */ + LARGE_INTEGER buf; - freq = ( QueryPerformanceFrequency( (LARGE_INTEGER *)&freq ) && - (freq == I64C(1193182) || freq == I64C(3579545) ) ) - ? freq : 0; + freq = ( QueryPerformanceFrequency( &buf ) && + (buf.QuadPart == I64C(1193182) || buf.QuadPart == I64C(3579545) ) ) + ? buf.QuadPart : 0; } if( freq != 0 ) { - /* Microsecond resolution */ - QueryPerformanceCounter( (LARGE_INTEGER *)&usec_time ); - return ( usec_time * 1000000 ) / freq; + LARGE_INTEGER counter; + QueryPerformanceCounter (&counter); + + /* 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); + + res = (d.quot * 1000000) + ((d.rem * 1000000) / freq); } else { @@ -174,30 +221,26 @@ mtime_t mdate( void ) } EnterCriticalSection( &date_lock ); - usec_time = I64C(1000) * + res = I64C(1000) * (i_wrap_counts * I64C(0x100000000) + GetTickCount()); - if( i_previous_time > usec_time ) + if( i_previous_time > res ) { /* Counter wrapped */ i_wrap_counts++; - usec_time += I64C(0x100000000000); + res += I64C(0x100000000) * 1000; } - i_previous_time = usec_time; + i_previous_time = res; LeaveCriticalSection( &date_lock ); - - return usec_time; } - #else struct timeval tv_date; - /* gettimeofday() could return an error, and should be tested. However, the - * only possible error, according to 'man', is EFAULT, which can not happen - * here, since tv is a local variable. */ - gettimeofday( &tv_date, NULL ); - return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec ); - + /* 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 cached_time = res; } /** @@ -210,72 +253,31 @@ mtime_t mdate( void ) */ void mwait( mtime_t date ) { -#if defined( HAVE_KERNEL_OS_H ) - mtime_t delay; + if( prec == 0 ) + prec = mprec(); - delay = date - real_time_clock_usecs(); - if( delay <= 0 ) - { + /* If the deadline is already elapsed, or within the clock precision, + * do not even bother the clock. */ + if( ( date - cached_time ) < (mtime_t)prec ) // OK: mtime_t is signed return; - } - snooze( delay ); -#elif defined( WIN32 ) || defined( UNDER_CE ) - mtime_t usec_time, delay; +#if 0 && defined (HAVE_CLOCK_NANOSLEEP) + lldiv_t d = lldiv( date, 1000000 ); + struct timespec ts = { d.quot, d.rem * 1000 }; - usec_time = mdate(); - delay = date - usec_time; - if( delay <= 0 ) + int val; + while( ( val = clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, + NULL ) ) == EINTR ); + if( val == EINVAL ) { - return; + ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000; + while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL ) == EINTR ); } - msleep( delay ); - #else - struct timeval tv_date; - mtime_t delay; /* delay in msec, signed to detect errors */ - - /* see mdate() about gettimeofday() possible errors */ - gettimeofday( &tv_date, NULL ); - - /* calculate delay and check if current date is before wished date */ - delay = date - (mtime_t) tv_date.tv_sec * 1000000 - - (mtime_t) tv_date.tv_usec - - 10000; - - /* Linux/i386 has a granularity of 10 ms. It's better to be in advance - * than to be late. */ - if( delay <= 0 ) /* wished date is now or already passed */ - { - return; - } - -# if defined( PTH_INIT_IN_PTH_H ) - pth_usleep( delay ); - -# elif defined( ST_INIT_IN_ST_H ) - st_usleep( delay ); - -# else - -# if defined( HAVE_NANOSLEEP ) - { - struct timespec ts_delay; - ts_delay.tv_sec = delay / 1000000; - ts_delay.tv_nsec = (delay % 1000000) * 1000; - - nanosleep( &ts_delay, NULL ); - } - -# else - tv_date.tv_sec = delay / 1000000; - tv_date.tv_usec = delay % 1000000; - /* see msleep() about select() errors */ - select( 0, NULL, NULL, NULL, &tv_date ); -# endif - -# endif + mtime_t delay = date - mdate(); + if( delay > 0 ) + msleep( delay ); #endif } @@ -288,7 +290,21 @@ void mwait( mtime_t date ) */ void msleep( mtime_t delay ) { -#if defined( HAVE_KERNEL_OS_H ) + mtime_t earlier = cached_time; + +#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( PTH_INIT_IN_PTH_H ) @@ -306,7 +322,7 @@ void msleep( mtime_t delay ) ts_delay.tv_sec = delay / 1000000; ts_delay.tv_nsec = (delay % 1000000) * 1000; - nanosleep( &ts_delay, NULL ); + while( nanosleep( &ts_delay, &ts_delay ) && ( errno == EINTR ) ); #else struct timeval tv_delay; @@ -314,13 +330,14 @@ void msleep( mtime_t delay ) tv_delay.tv_sec = delay / 1000000; tv_delay.tv_usec = delay % 1000000; - /* select() return value should be tested, since several possible errors - * can occur. However, they should only happen in very particular occasions - * (i.e. when a signal is sent to the thread, or when memory is full), and - * can be ignored. */ + /* 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 + + earlier += delay; + if( cached_time < earlier ) + cached_time = earlier; } /* @@ -414,3 +431,79 @@ mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples ) return p_date->date; } + +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 + +/* + * Number of micro-seconds between the beginning of the Windows epoch + * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970). + * + * This assumes all Win32 compilers have 64-bit support. + */ +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__WATCOMC__) +# define DELTA_EPOCH_IN_USEC 11644473600000000Ui64 +#else +# define DELTA_EPOCH_IN_USEC 11644473600000000ULL +#endif + +static uint64_t filetime_to_unix_epoch (const FILETIME *ft) +{ + uint64_t res = (uint64_t) ft->dwHighDateTime << 32; + + res |= ft->dwLowDateTime; + res /= 10; /* from 100 nano-sec periods to usec */ + res -= DELTA_EPOCH_IN_USEC; /* from Win epoch to Unix epoch */ + return (res); +} + +static int gettimeofday (struct timeval *tv, void *tz ) +{ + FILETIME ft; + uint64_t tim; + + if (!tv) { + return VLC_EGENERIC; + } + GetSystemTimeAsFileTime (&ft); + tim = filetime_to_unix_epoch (&ft); + tv->tv_sec = (long) (tim / 1000000L); + tv->tv_usec = (long) (tim % 1000000L); + return (0); +} + +#endif + +#endif + +/** + * @return NTP 64-bits timestamp in host byte order. + */ +uint64_t NTPtime64 (void) +{ + struct timespec ts; +#if defined (CLOCK_REALTIME) + clock_gettime (CLOCK_REALTIME, &ts); +#else + { + struct timeval tv; + 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) */ + uint64_t t = (uint64_t)(ts.tv_nsec) << 32; + t /= 1000000000; + + + /* There is 70 years (incl. 17 leap ones) offset to the Unix Epoch. + * No leap seconds during that period since they were not invented yet. + */ + assert (t < 0x100000000); + t |= ((70LL * 365 + 17) * 24 * 60 * 60 + ts.tv_sec) << 32; + return t; +} +