X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fmtime.c;h=9e22a701e07f96f6adb99a238d1e3dd898250755;hb=a08b518d34d34b4d7f1377e23fd3c09d31bd248b;hp=2b27fbf40a05ecd74c17c0c90bb38a744dcbf3bf;hpb=076dc4357cbe4eec9085a59162c6910a07d0b850;p=vlc diff --git a/src/misc/mtime.c b/src/misc/mtime.c index 2b27fbf40a..9e22a701e0 100644 --- a/src/misc/mtime.c +++ b/src/misc/mtime.c @@ -2,7 +2,7 @@ * mtime.c: high resolution time management functions * Functions are prototyped in vlc_mtime.h. ***************************************************************************** - * Copyright (C) 1998-2007 the VideoLAN team + * Copyright (C) 1998-2007 VLC authors and VideoLAN * Copyright © 2006-2007 Rémi Denis-Courmont * $Id$ * @@ -10,19 +10,19 @@ * 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 - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -34,56 +34,18 @@ #endif #include - -#include /* clock_gettime(), clock_nanosleep() */ #include -#include #ifdef HAVE_UNISTD_H -# include /* select() */ -#endif - -#ifdef HAVE_KERNEL_OS_H -# include -#endif - -#if defined( WIN32 ) || defined( UNDER_CE ) -# include -# include +# include #endif - -#if defined(HAVE_SYS_TIME_H) -# include -#endif - -#if !defined(HAVE_STRUCT_TIMESPEC) -struct timespec -{ - time_t tv_sec; - int32_t tv_nsec; -}; +#if !defined (_POSIX_TIMERS) || defined (_WIN32) +# define _POSIX_TIMERS (-1) #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) -#endif - -# if (_POSIX_CLOCK_SELECTION < 0) -/* - * We cannot use the monotonic clock is 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 - * 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 (_POSIX_TIMERS > 0) +# include /* clock_gettime() */ +#else +# include #endif /** @@ -119,325 +81,28 @@ 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 ) -{ - int i_hours, i_mins; - i_mins = i_seconds / 60; - i_hours = i_mins / 60 ; - if( i_hours ) - { - snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d", - (int) i_hours, - (int) (i_mins % 60), - (int) (i_seconds % 60) ); - } - else - { - snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%2.2d:%2.2d", - (int) i_mins , - (int) (i_seconds % 60) ); - } - 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 ) +char *secstotimestr( char *psz_buffer, int32_t i_seconds ) { -#if defined (HAVE_CLOCK_NANOSLEEP) - static pthread_once_t once = PTHREAD_ONCE_INIT; - pthread_once( &once, mprec_once ); - return prec; -#else - return 0; -#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( WIN32 ) || defined( UNDER_CE ) - /* We don't need the real date, just the value of a high precision timer */ - static mtime_t freq = INT64_C(-1); - - if( freq == INT64_C(-1) ) + if( unlikely(i_seconds < 0) ) { - /* 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 == INT64_C(1193182) || buf.QuadPart == INT64_C(3579545) ) ) - ? buf.QuadPart : 0; - -#if defined( WIN32 ) - /* on windows 2000, XP and Vista detect if there are two - cores there - that makes QueryPerformanceFrequency in - any case not trustable? - (may also be true, for single cores with adaptive - CPU frequency and active power management?) - */ - HINSTANCE h_Kernel32 = LoadLibrary(_T("kernel32.dll")); - if(h_Kernel32) - { - void WINAPI (*pf_GetSystemInfo)(LPSYSTEM_INFO); - pf_GetSystemInfo = (void WINAPI (*)(LPSYSTEM_INFO)) - GetProcAddress(h_Kernel32, _T("GetSystemInfo")); - if(pf_GetSystemInfo) - { - SYSTEM_INFO system_info; - pf_GetSystemInfo(&system_info); - if(system_info.dwNumberOfProcessors > 1) - freq = 0; - } - FreeLibrary(h_Kernel32); - } -#endif + secstotimestr( psz_buffer + 1, -i_seconds ); + *psz_buffer = '-'; + return psz_buffer; } - if( freq != 0 ) - { - LARGE_INTEGER counter; - QueryPerformanceCounter (&counter); + div_t d; - /* 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); + d = div( i_seconds, 60 ); + i_seconds = d.rem; + d = div( d.quot, 60 ); - res = (d.quot * 1000000) + ((d.rem * 1000000) / freq); - } + if( d.quot ) + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u", + d.quot, d.rem, i_seconds ); else - { - /* Fallback on timeGetTime() which has a millisecond resolution - * (actually, best case is about 5 ms resolution) - * timeGetTime() 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 = INT64_C(-1); - static int i_wrap_counts = -1; - - if( i_wrap_counts == -1 ) - { - /* Initialization */ -#if defined( WIN32 ) - i_previous_time = INT64_C(1000) * timeGetTime(); -#else - i_previous_time = INT64_C(1000) * GetTickCount(); -#endif - InitializeCriticalSection( &date_lock ); - i_wrap_counts = 0; - } - - EnterCriticalSection( &date_lock ); -#if defined( WIN32 ) - res = INT64_C(1000) * - (i_wrap_counts * INT64_C(0x100000000) + timeGetTime()); -#else - res = INT64_C(1000) * - (i_wrap_counts * INT64_C(0x100000000) + GetTickCount()); -#endif - if( i_previous_time > res ) - { - /* Counter wrapped */ - i_wrap_counts++; - res += INT64_C(0x100000000) * 1000; - } - i_previous_time = res; - LeaveCriticalSection( &date_lock ); - } -#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(); - -#elif defined( __APPLE__ ) - /* Explicit hack: OSX does not cancel at nanosleep() */ - vlc_mutex_t lock; - vlc_cond_t wait; - - vlc_mutex_init (&lock); - vlc_cond_init (&wait); - vlc_mutex_lock (&lock); - - vlc_cleanup_push (vlc_mutex_destroy, &lock); - vlc_cleanup_push (vlc_cond_destroy, &wait); - vlc_cleanup_push (vlc_mutex_unlock, &lock); - - vlc_cond_timedwait (&wait, &lock, date); - - vlc_cleanup_run (); - vlc_cleanup_run (); - vlc_cleanup_run (); - -#else - mtime_t delay = date - mdate(); - if( delay > 0 ) - msleep( delay ); - -#endif -} - - -#include "libvlc.h" /* vlc_backtrace() */ -#undef msleep - -#if defined(__APPLE__) && defined( HAVE_NANOSLEEP ) -/* Mac OS X 10.5's nanosleep is not a cancellation point */ -static inline int -semi_testcancelable_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) -{ - int ret; - pthread_testcancel(); - ret = nanosleep(rqtp, rmtp); - pthread_testcancel(); - return ret; -} -#define nanosleep semi_testcancelable_nanosleep -#endif - -/** - * 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 ) || defined( __APPLE__ ) - 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 ) ); - -#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 + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u", + d.rem, i_seconds ); + return psz_buffer; } /* @@ -520,9 +185,11 @@ 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->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); + mtime_t i_dividend = i_nb_samples * CLOCK_FREQ * p_date->i_divider_den; + lldiv_t d = lldiv( i_dividend, p_date->i_divider_num ); + + p_date->date += d.quot; + p_date->i_remainder += (int)d.rem; if( p_date->i_remainder >= p_date->i_divider_num ) { @@ -535,66 +202,53 @@ 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). +/** + * Decrement the date and return the result, taking into account + * rounding errors. * - * This assumes all Win32 compilers have 64-bit support. + * \param date to decrement + * \param decrementation in number of samples + * \return date value */ -#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) +mtime_t date_Decrement( date_t *p_date, uint32_t i_nb_samples ) { - 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; + 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 (!tv) { - return VLC_EGENERIC; + 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; } - GetSystemTimeAsFileTime (&ft); - tim = filetime_to_unix_epoch (&ft); - tv->tv_sec = (long) (tim / 1000000L); - tv->tv_usec = (long) (tim % 1000000L); - return (0); -} -#endif + p_date->i_remainder -= i_rem_adjust; -#endif + return p_date->date; +} /** * @return NTP 64-bits timestamp in host byte order. */ uint64_t NTPtime64 (void) { +#if (_POSIX_TIMERS > 0) struct timespec ts; -#if defined (CLOCK_REALTIME) + 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) */