X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fmtime.c;h=115dbe9ffc6cce42a2ef2e9a5dde211a035b9fd1;hb=599fbde71fba4397aa62fe81f957ee641654997c;hp=fc18906ce9d1fc31f2d25ae05c2fcb848451391b;hpb=253fe31c5a1b4ea79638dd545f36f1bb7afa98cb;p=vlc diff --git a/src/misc/mtime.c b/src/misc/mtime.c index fc18906ce9..115dbe9ffc 100644 --- a/src/misc/mtime.c +++ b/src/misc/mtime.c @@ -29,17 +29,16 @@ * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include #include /* clock_gettime(), clock_nanosleep() */ #include #include - -#if defined( PTH_INIT_IN_PTH_H ) /* GNU Pth */ -# include -#endif - #ifdef HAVE_UNISTD_H # include /* select() */ #endif @@ -50,11 +49,19 @@ #if defined( WIN32 ) || defined( UNDER_CE ) # include +# include #endif + #if defined(HAVE_SYS_TIME_H) # include #endif +#if defined(__APPLE__) && !defined(__powerpc__) && !defined(__ppc__) && !defined(__ppc64__) +#define USE_APPLE_MACH 1 +# include +# include +#endif + #if !defined(HAVE_STRUCT_TIMESPEC) struct timespec { @@ -67,17 +74,22 @@ struct timespec int nanosleep(struct timespec *, struct timespec *); #endif -#ifdef HAVE_CLOCK_NANOSLEEP -# if !defined _POSIX_CLOCK_SELECTION || (_POSIX_CLOCK_SELECTION - 0 <= 0) +#if !defined (_POSIX_CLOCK_SELECTION) +# define _POSIX_CLOCK_SELECTION (-1) +#endif + +# if (_POSIX_CLOCK_SELECTION < 0) /* - * We cannot use the monotonic clock is clock selection is not available, + * 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 when ntpdate + * stick to the realtime clock. Nevermind it screws everything up when ntpdate * warps the wall clock. */ -# undef CLOCK_MONOTONIC -# define CLOCK_MONOTONIC CLOCK_REALTIME -# endif +# 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. #endif /** @@ -92,7 +104,7 @@ int nanosleep(struct timespec *, struct timespec *); */ char *mstrtime( char *psz_buffer, mtime_t date ) { - static mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24; + static const mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24; snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d", (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24), @@ -113,27 +125,43 @@ char *mstrtime( char *psz_buffer, mtime_t date ) * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters * \return psz_buffer is returned so this can be used as printf parameter. */ -char *secstotimestr( char *psz_buffer, int i_seconds ) +char *secstotimestr( char *psz_buffer, int32_t i_seconds ) { - int i_hours, i_mins; - i_mins = i_seconds / 60; - i_hours = i_mins / 60 ; - if( i_hours ) + if( unlikely(i_seconds < 0) ) { - snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d", - (int) i_hours, - (int) (i_mins % 60), - (int) (i_seconds % 60) ); + secstotimestr( psz_buffer + 1, -i_seconds ); + *psz_buffer = '-'; + return psz_buffer; } + + div_t d; + + d = div( i_seconds, 60 ); + i_seconds = d.rem; + d = div( d.quot, 60 ); + + if( d.quot ) + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u", + d.quot, d.rem, i_seconds ); else - { - snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%2.2d:%2.2d", - (int) i_mins , - (int) (i_seconds % 60) ); - } - return( psz_buffer ); + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u", + d.rem, 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). @@ -141,23 +169,29 @@ char *secstotimestr( char *psz_buffer, int i_seconds ) 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 + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once( &once, mprec_once ); + return prec; +#else return 0; +#endif } -static unsigned prec = 0; -static volatile mtime_t cached_time = 0; +#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 * - * Uses the gettimeofday() function when possible (1 MHz resolution) or the - * ftime() function (1 kHz resolution). + * 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 ) { @@ -177,79 +211,35 @@ mtime_t mdate( void ) #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 */ - static mtime_t freq = I64C(-1); + LARGE_INTEGER counter, freq; + if (!QueryPerformanceCounter (&counter) + || !QueryPerformanceFrequency (&freq)) + abort(); - if( freq == I64C(-1) ) - { - /* Extract from the Tcl source code: - * (http://www.cs.man.ac.uk/fellowsd-bin/TIP/7.html) - * - * Some hardware abstraction layers use the CPU clock - * in place of the real-time clock as a performance counter - * reference. This results in: - * - inconsistent results among the processors on - * multi-processor systems. - * - unpredictable changes in performance counter frequency - * on "gearshift" processors such as Transmeta and - * SpeedStep. - * There seems to be no way to test whether the performance - * counter is reliable, but a useful heuristic is that - * if its frequency is 1.193182 MHz or 3.579545 MHz, it's - * derived from a colorburst crystal and is therefore - * the RTC rather than the TSC. If it's anything else, we - * presume that the performance counter is unreliable. - */ - LARGE_INTEGER buf; - - freq = ( QueryPerformanceFrequency( &buf ) && - (buf.QuadPart == I64C(1193182) || buf.QuadPart == I64C(3579545) ) ) - ? buf.QuadPart : 0; - } + /* 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); - if( freq != 0 ) - { - LARGE_INTEGER counter; - QueryPerformanceCounter (&counter); + res = (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart); - /* 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 - { - /* Fallback on GetTickCount() which has a milisecond resolution - * (actually, best case is about 10 ms resolution) - * GetTickCount() only returns a DWORD thus will wrap after - * about 49.7 days so we try to detect the wrapping. */ - - static CRITICAL_SECTION date_lock; - static mtime_t i_previous_time = I64C(-1); - static int i_wrap_counts = -1; - - if( i_wrap_counts == -1 ) - { - /* Initialization */ - i_previous_time = I64C(1000) * GetTickCount(); - InitializeCriticalSection( &date_lock ); - i_wrap_counts = 0; - } - - EnterCriticalSection( &date_lock ); - res = I64C(1000) * - (i_wrap_counts * I64C(0x100000000) + GetTickCount()); - if( i_previous_time > res ) - { - /* Counter wrapped */ - i_wrap_counts++; - res += I64C(0x100000000) * 1000; - } - i_previous_time = res; - LeaveCriticalSection( &date_lock ); - } #else struct timeval tv_date; @@ -258,9 +248,10 @@ mtime_t mdate( void ) res = (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec; #endif - return cached_time = res; + return res; } +#undef mwait /** * Wait for a date * @@ -271,15 +262,11 @@ mtime_t mdate( void ) */ void mwait( mtime_t date ) { - if( prec == 0 ) - prec = mprec(); - /* 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; + * do not even bother the system timer. */ + date -= mprec(); -#if 0 && defined (HAVE_CLOCK_NANOSLEEP) +#if defined (HAVE_CLOCK_NANOSLEEP) lldiv_t d = lldiv( date, 1000000 ); struct timespec ts = { d.quot, d.rem * 1000 }; @@ -291,8 +278,20 @@ void mwait( mtime_t date ) ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000; while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL ) == EINTR ); } -#else +#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 ); @@ -300,16 +299,17 @@ void mwait( mtime_t date ) #endif } + +#include "libvlc.h" /* vlc_backtrace() */ +#undef msleep + /** - * More precise sleep() + * Portable usleep(). Cancellation point. * - * Portable usleep() function. * \param delay the amount of time to sleep */ void msleep( mtime_t delay ) { - mtime_t earlier = cached_time; - #if defined( HAVE_CLOCK_NANOSLEEP ) lldiv_t d = lldiv( delay, 1000000 ); struct timespec ts = { d.quot, d.rem * 1000 }; @@ -325,14 +325,8 @@ void msleep( mtime_t delay ) #elif defined( HAVE_KERNEL_OS_H ) snooze( delay ); -#elif defined( PTH_INIT_IN_PTH_H ) - pth_usleep( delay ); - -#elif defined( ST_INIT_IN_ST_H ) - st_usleep( delay ); - #elif defined( WIN32 ) || defined( UNDER_CE ) - Sleep( (int) (delay / 1000) ); + mwait (mdate () + delay); #elif defined( HAVE_NANOSLEEP ) struct timespec ts_delay; @@ -342,6 +336,12 @@ void msleep( mtime_t delay ) 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; @@ -352,10 +352,6 @@ void msleep( mtime_t delay ) * 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; } /* @@ -388,6 +384,8 @@ void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) { + /* change time scale of remainder */ + p_date->i_remainder = p_date->i_remainder * i_divider_n / p_date->i_divider_num; p_date->i_divider_num = i_divider_n; p_date->i_divider_den = i_divider_d; } @@ -436,20 +434,48 @@ void date_Move( date_t *p_date, mtime_t i_difference ) */ mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples ) { - mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000; - p_date->date += i_dividend / p_date->i_divider_num * p_date->i_divider_den; + mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000 * p_date->i_divider_den; + p_date->date += i_dividend / p_date->i_divider_num; p_date->i_remainder += (int)(i_dividend % p_date->i_divider_num); if( p_date->i_remainder >= p_date->i_divider_num ) { /* This is Bresenham algorithm. */ - p_date->date += p_date->i_divider_den; + assert( p_date->i_remainder < 2*p_date->i_divider_num); + p_date->date += 1; p_date->i_remainder -= p_date->i_divider_num; } return p_date->date; } +/** + * Decrement the date and return the result, taking into account + * rounding errors. + * + * \param date to decrement + * \param decrementation in number of samples + * \return date value + */ +mtime_t date_Decrement( date_t *p_date, uint32_t i_nb_samples ) +{ + mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000 * p_date->i_divider_den; + p_date->date -= i_dividend / p_date->i_divider_num; + unsigned i_rem_adjust = i_dividend % p_date->i_divider_num; + + if( p_date->i_remainder < i_rem_adjust ) + { + /* This is Bresenham algorithm. */ + assert( p_date->i_remainder > -p_date->i_divider_num); + p_date->date -= 1; + p_date->i_remainder += p_date->i_divider_num; + } + + p_date->i_remainder -= i_rem_adjust; + + return p_date->date; +} + #ifndef HAVE_GETTIMEOFDAY #ifdef WIN32