* Preamble
*****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include <vlc/vlc.h>
-#include <stdio.h> /* sprintf() */
#include <time.h> /* clock_gettime(), clock_nanosleep() */
-#include <stdlib.h> /* lldiv() */
#include <assert.h>
-
-
-#if defined( PTH_INIT_IN_PTH_H ) /* GNU Pth */
-# include <pth.h>
-#endif
+#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* select() */
#if defined( WIN32 ) || defined( UNDER_CE )
# include <windows.h>
-#else
+# include <mmsystem.h>
+#endif
+
+#if defined( UNDER_CE )
+# include <windows.h>
+#endif
+
+#if defined(HAVE_SYS_TIME_H)
# include <sys/time.h>
#endif
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.
+#endif
+
/**
* Return a date in a readable format
*
*/
char *secstotimestr( char *psz_buffer, int i_seconds )
{
- snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d",
- (int) (i_seconds / (60 *60)),
- (int) ((i_seconds / 60) % 60),
- (int) (i_seconds % 60) );
+ 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 );
}
+/**
+ * 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;
/**
* 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 )
{
+ mtime_t res;
+
#if defined (HAVE_CLOCK_NANOSLEEP)
struct timespec ts;
-# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
/* Try to use POSIX monotonic clock if available */
- if( clock_gettime( CLOCK_MONOTONIC, &ts ) )
-# endif
+ if( clock_gettime( CLOCK_MONOTONIC, &ts ) == EINVAL )
/* Run-time fallback to real-time clock (always available) */
(void)clock_gettime( CLOCK_REALTIME, &ts );
- return ((mtime_t)ts.tv_sec * (mtime_t)1000000)
+ res = ((mtime_t)ts.tv_sec * (mtime_t)1000000)
+ (mtime_t)(ts.tv_nsec / 1000);
#elif defined( HAVE_KERNEL_OS_H )
- return( real_time_clock_usecs() );
+ 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 */
freq = ( QueryPerformanceFrequency( &buf ) &&
(buf.QuadPart == I64C(1193182) || buf.QuadPart == I64C(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 = LoadLibraryA("kernel32.dll");
+ if(h_Kernel32)
+ {
+ void WINAPI (*pf_GetSystemInfo)(LPSYSTEM_INFO*);
+ pf_GetSystemInfo = (void WINAPI (*)(LPSYSTEM_INFO*))
+ GetProcAddress(h_Kernel32, "GetSystemInfo");
+ if(pf_GetSystemInfo)
+ {
+ SYSTEM_INFO system_info;
+ pf_GetSystemInfo(&system_info);
+ if(system_info.dwNumberOfProcessors > 1)
+ freq = 0;
+ }
+ FreeLibrary(h_Kernel32);
+ }
+#endif
}
if( freq != 0 )
/* We need to split the division to avoid 63-bits overflow */
lldiv_t d = lldiv (counter.QuadPart, freq);
- return (d.quot * 1000000)
- + ((d.rem * 1000000) / 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
+ /* Fallback on timeGetTime() which has a milisecond 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 = I64C(-1);
static int i_wrap_counts = -1;
- mtime_t usec_time;
if( i_wrap_counts == -1 )
{
/* Initialization */
+#if defined( WIN32 )
+ i_previous_time = I64C(1000) * timeGetTime();
+#else
i_previous_time = I64C(1000) * GetTickCount();
+#endif
InitializeCriticalSection( &date_lock );
i_wrap_counts = 0;
}
EnterCriticalSection( &date_lock );
- usec_time = I64C(1000) *
+#if defined( WIN32 )
+ res = I64C(1000) *
+ (i_wrap_counts * I64C(0x100000000) + timeGetTime());
+#else
+ res = I64C(1000) *
(i_wrap_counts * I64C(0x100000000) + GetTickCount());
- if( i_previous_time > usec_time )
+#endif
+ if( i_previous_time > res )
{
/* Counter wrapped */
i_wrap_counts++;
- usec_time += I64C(0x100000000) * 1000;
+ 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() cannot fail given &tv_date is a valid address */
(void)gettimeofday( &tv_date, NULL );
- return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec );
+ res = (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
#endif
+
+ return cached_time = res;
}
/**
*/
void mwait( mtime_t date )
{
-#if defined (HAVE_CLOCK_NANOSLEEP)
+ 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;
+
+#if 0 && defined (HAVE_CLOCK_NANOSLEEP)
lldiv_t d = lldiv( date, 1000000 );
struct timespec ts = { d.quot, d.rem * 1000 };
-# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
- if( clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL ) )
-# endif
- clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL );
+ 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 );
+ }
#else
mtime_t delay = date - mdate();
*/
void msleep( mtime_t delay )
{
-#if defined( HAVE_CLOCK_NANOSLEEP )
+ mtime_t earlier = cached_time;
+
+#if defined( HAVE_CLOCK_NANOSLEEP )
lldiv_t d = lldiv( delay, 1000000 );
struct timespec ts = { d.quot, d.rem * 1000 };
-# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
- if( clock_nanosleep( CLOCK_MONOTONIC, 0, &ts, NULL ) )
-# endif
- clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL );
+ 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 )
- pth_usleep( delay );
-
-#elif defined( ST_INIT_IN_ST_H )
- st_usleep( delay );
-
#elif defined( WIN32 ) || defined( UNDER_CE )
- Sleep( (int) (delay / 1000) );
+ Sleep( (DWORD) (delay / 1000) );
#elif defined( HAVE_NANOSLEEP )
struct timespec ts_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;
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;
}
/*
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).
tv->tv_usec = (long) (tim % 1000000L);
return (0);
}
-#endif
+#endif
+#endif
/**
* @return NTP 64-bits timestamp in host byte order.