mtime.c 14.9 KB
Newer Older
1
/*****************************************************************************
2
 * mtime.c: high resolution time management functions
zorglub's avatar
zorglub committed
3
 * Functions are prototyped in vlc_mtime.h.
4
 *****************************************************************************
5
 * Copyright (C) 1998-2007 the VideoLAN team
6
 * Copyright © 2006-2007 Rémi Denis-Courmont
7
 * $Id$
8
 *
9
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10
 *          Rémi Denis-Courmont <rem$videolan,org>
11
 *          Gisle Vanem
12
13
14
15
16
 *
 * 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
 * (at your option) any later version.
zorglub's avatar
zorglub committed
17
 *
18
19
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
22
 *
23
24
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
27

28
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
29
 * Preamble
30
 *****************************************************************************/
31

32
33
34
35
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

36
#include <vlc_common.h>
37

38
#include <time.h>                      /* clock_gettime(), clock_nanosleep() */
39
#include <assert.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
40
#include <errno.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
41
42

#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
   
Sam Hocevar committed
43
#   include <unistd.h>                                           /* select() */
Sam Hocevar's avatar
   
Sam Hocevar committed
44
#endif
Michel Kaempf's avatar
Michel Kaempf committed
45

46
#ifdef HAVE_KERNEL_OS_H
Sam Hocevar's avatar
   
Sam Hocevar committed
47
#   include <kernel/OS.h>
48
49
#endif

50
#if defined( WIN32 ) || defined( UNDER_CE )
Sam Hocevar's avatar
   
Sam Hocevar committed
51
#   include <windows.h>
52
#   include <mmsystem.h>
53
#endif
54

55
#if defined(HAVE_SYS_TIME_H)
Sam Hocevar's avatar
   
Sam Hocevar committed
56
#   include <sys/time.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
57
58
#endif

59
#if defined(__APPLE__) && !defined(__powerpc__) && !defined(__ppc__) && !defined(__ppc64__)
60
#define USE_APPLE_MACH 1
61
62
63
64
#   include <mach/mach.h>
#   include <mach/mach_time.h>
#endif

65
#if !defined(HAVE_STRUCT_TIMESPEC)
66
67
struct timespec
{
68
69
    time_t  tv_sec;
    int32_t tv_nsec;
70
71
72
73
};
#endif

#if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP)
74
int nanosleep(struct timespec *, struct timespec *);
75
76
#endif

77
78
79
80
81
#if !defined (_POSIX_CLOCK_SELECTION)
#  define _POSIX_CLOCK_SELECTION (-1)
#endif

# if (_POSIX_CLOCK_SELECTION < 0)
82
/*
Rémi Denis-Courmont's avatar
Grammar    
Rémi Denis-Courmont committed
83
 * We cannot use the monotonic clock if clock selection is not available,
84
 * as it would screw vlc_cond_timedwait() completely. Instead, we have to
Rémi Denis-Courmont's avatar
Typo    
Rémi Denis-Courmont committed
85
 * stick to the realtime clock. Nevermind it screws everything up when ntpdate
86
87
 * warps the wall clock.
 */
88
89
#  undef CLOCK_MONOTONIC
#  define CLOCK_MONOTONIC CLOCK_REALTIME
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
90
91
92
#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.
93
94
#endif

sigmunau's avatar
sigmunau committed
95
96
97
/**
 * Return a date in a readable format
 *
98
99
 * This function converts a mtime date into a string.
 * psz_buffer should be a buffer long enough to store the formatted
zorglub's avatar
zorglub committed
100
 * date.
101
102
103
104
 * \param date to be converted
 * \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.
 */
Michel Kaempf's avatar
Michel Kaempf committed
105
106
char *mstrtime( char *psz_buffer, mtime_t date )
{
107
    static const mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24;
108

109
    snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d",
110
111
112
113
114
             (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24),
             (int) (date / (ll1000 * ll1000 * ll60) % ll60),
             (int) (date / (ll1000 * ll1000) % ll60),
             (int) (date / ll1000 % ll1000),
             (int) (date % ll1000) );
Michel Kaempf's avatar
Michel Kaempf committed
115
116
117
    return( psz_buffer );
}

sigmunau's avatar
sigmunau committed
118
119
120
/**
 * Convert seconds to a time in the format h:mm:ss.
 *
121
122
123
124
125
126
127
 * This function is provided for any interface function which need to print a
 * time string in the format h:mm:ss
 * date.
 * \param secs  the date to be converted
 * \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.
 */
128
char *secstotimestr( char *psz_buffer, int32_t i_seconds )
129
{
130
131
132
133
134
135
136
    if( unlikely(i_seconds < 0) )
    {
        secstotimestr( psz_buffer + 1, -i_seconds );
        *psz_buffer = '-';
        return psz_buffer;
    }

137
138
139
140
141
142
143
    div_t d;

    d = div( i_seconds, 60 );
    i_seconds = d.rem;
    d = div( d.quot, 60 );

    if( d.quot )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
144
        snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u",
145
                 d.quot, d.rem, i_seconds );
146
    else
147
148
        snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u",
                  d.rem, i_seconds );
149
    return psz_buffer;
150
151
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152
153
154
155
156
157
158
159
160
161
162
163
164
#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

165
166
167
168
169
170
171
/**
 * Return a value that is no bigger than the clock precision
 * (possibly zero).
 */
static inline unsigned mprec( void )
{
#if defined (HAVE_CLOCK_NANOSLEEP)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
172
173
174
175
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once( &once, mprec_once );
    return prec;
#else
176
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177
#endif
178
179
}

180
#ifdef USE_APPLE_MACH
181
182
183
184
185
186
187
188
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

sigmunau's avatar
sigmunau committed
189
190
191
/**
 * Return high precision date
 *
Rafaël Carré's avatar
Rafaël Carré committed
192
193
194
 * 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)
sigmunau's avatar
sigmunau committed
195
 */
Michel Kaempf's avatar
Michel Kaempf committed
196
mtime_t mdate( void )
Michel Kaempf's avatar
Michel Kaempf committed
197
{
198
199
    mtime_t res;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
200
201
202
203
#if defined (HAVE_CLOCK_NANOSLEEP)
    struct timespec ts;

    /* Try to use POSIX monotonic clock if available */
204
    if( clock_gettime( CLOCK_MONOTONIC, &ts ) == EINVAL )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
205
206
207
        /* Run-time fallback to real-time clock (always available) */
        (void)clock_gettime( CLOCK_REALTIME, &ts );

208
    res = ((mtime_t)ts.tv_sec * (mtime_t)1000000)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
209
210
211
           + (mtime_t)(ts.tv_nsec / 1000);

#elif defined( HAVE_KERNEL_OS_H )
212
    res = real_time_clock_usecs();
Sam Hocevar's avatar
   
Sam Hocevar committed
213

214
#elif defined( USE_APPLE_MACH )
215
216
    pthread_once(&mtime_timebase_info_once, mtime_init_timebase);
    uint64_t date = mach_absolute_time();
217
    mach_timebase_info_data_t tb = mtime_timebase_info;
218

219
220
221
222
223
224
225
226
227
228
    /* 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);
229

230
#elif defined( WIN32 ) || defined( UNDER_CE )
gbazin's avatar
   
gbazin committed
231
    /* We don't need the real date, just the value of a high precision timer */
232
233
234
235
    LARGE_INTEGER counter, freq;
    if (!QueryPerformanceCounter (&counter)
     || !QueryPerformanceFrequency (&freq))
        abort();
Sam Hocevar's avatar
   
Sam Hocevar committed
236

237
238
239
    /* 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);
gbazin's avatar
   
gbazin committed
240

241
    res = (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
242

243
#else
Michel Kaempf's avatar
Michel Kaempf committed
244
245
    struct timeval tv_date;

246
247
    /* gettimeofday() cannot fail given &tv_date is a valid address */
    (void)gettimeofday( &tv_date, NULL );
248
    res = (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec;
249
#endif
250

251
    return res;
Michel Kaempf's avatar
Michel Kaempf committed
252
253
}

254
#undef mwait
sigmunau's avatar
sigmunau committed
255
256
257
/**
 * Wait for a date
 *
Michel Kaempf's avatar
Michel Kaempf committed
258
259
260
 * 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.
sigmunau's avatar
sigmunau committed
261
262
 * \param date The date to wake up at
 */
Michel Kaempf's avatar
Michel Kaempf committed
263
264
void mwait( mtime_t date )
{
265
    /* If the deadline is already elapsed, or within the clock precision,
266
267
     * do not even bother the system timer. */
    date -= mprec();
268

269
#if defined (HAVE_CLOCK_NANOSLEEP)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
270
    lldiv_t d = lldiv( date, 1000000 );
271
    struct timespec ts = { d.quot, d.rem * 1000 };
272

273
274
275
276
277
278
279
280
    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 );
    }
281

282
#elif defined (WIN32)
Laurent Aimar's avatar
Laurent Aimar committed
283
    mtime_t i_total;
284

Laurent Aimar's avatar
Laurent Aimar committed
285
    while( (i_total = (date - mdate())) > 0 )
286
    {
Laurent Aimar's avatar
Laurent Aimar committed
287
288
289
290
        const mtime_t i_sleep = i_total / 1000;
        DWORD i_delay = (i_sleep > 0x7fffffff) ? 0x7fffffff : i_sleep;
        vlc_testcancel();
        SleepEx( i_delay, TRUE );
291
    }
Laurent Aimar's avatar
Laurent Aimar committed
292
    vlc_testcancel();
293
294

#else
295
    mtime_t delay = date - mdate();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
296
297
298
    if( delay > 0 )
        msleep( delay );

Sam Hocevar's avatar
   
Sam Hocevar committed
299
#endif
Michel Kaempf's avatar
Michel Kaempf committed
300
301
}

302
303

#include "libvlc.h" /* vlc_backtrace() */
304
#undef msleep
305

sigmunau's avatar
sigmunau committed
306
/**
307
 * Portable usleep(). Cancellation point.
sigmunau's avatar
sigmunau committed
308
309
310
 *
 * \param delay the amount of time to sleep
 */
Michel Kaempf's avatar
Michel Kaempf committed
311
312
void msleep( mtime_t delay )
{
313
#if defined( HAVE_CLOCK_NANOSLEEP )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
314
315
316
    lldiv_t d = lldiv( delay, 1000000 );
    struct timespec ts = { d.quot, d.rem * 1000 };

317
318
319
320
321
322
323
    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 );
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
324
325

#elif defined( HAVE_KERNEL_OS_H )
326
327
    snooze( delay );

328
#elif defined( WIN32 ) || defined( UNDER_CE )
329
    mwait (mdate () + delay);
gbazin's avatar
   
gbazin committed
330

331
332
333
334
335
336
#elif defined( HAVE_NANOSLEEP )
    struct timespec ts_delay;

    ts_delay.tv_sec = delay / 1000000;
    ts_delay.tv_nsec = (delay % 1000000) * 1000;

337
    while( nanosleep( &ts_delay, &ts_delay ) && ( errno == EINTR ) );
338

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
340
#elif defined (USE_APPLE_MACH)
    /* The version that should be used, if it was cancelable */
341
342
343
344
    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());

345
#else
Michel Kaempf's avatar
Michel Kaempf committed
346
347
348
349
    struct timeval tv_delay;

    tv_delay.tv_sec = delay / 1000000;
    tv_delay.tv_usec = delay % 1000000;
350

351
352
    /* If a signal is caught, you are screwed. Update your OS to nanosleep()
     * or clock_nanosleep() if this is an issue. */
Michel Kaempf's avatar
Michel Kaempf committed
353
    select( 0, NULL, NULL, NULL, &tv_delay );
Sam Hocevar's avatar
   
Sam Hocevar committed
354
#endif
Michel Kaempf's avatar
Michel Kaempf committed
355
}
Sam Hocevar's avatar
   
Sam Hocevar committed
356

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*
 * Date management (internal and external)
 */

/**
 * Initialize a date_t.
 *
 * \param date to initialize
 * \param divider (sample rate) numerator
 * \param divider (sample rate) denominator
 */

void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
{
    p_date->date = 0;
    p_date->i_divider_num = i_divider_n;
    p_date->i_divider_den = i_divider_d;
    p_date->i_remainder = 0;
}

/**
 * Change a date_t.
 *
 * \param date to change
 * \param divider (sample rate) numerator
 * \param divider (sample rate) denominator
 */

void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
{
387
388
    /* change time scale of remainder */
    p_date->i_remainder = p_date->i_remainder * i_divider_n / p_date->i_divider_num;
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
    p_date->i_divider_num = i_divider_n;
    p_date->i_divider_den = i_divider_d;
}

/**
 * Set the date value of a date_t.
 *
 * \param date to set
 * \param date value
 */
void date_Set( date_t *p_date, mtime_t i_new_date )
{
    p_date->date = i_new_date;
    p_date->i_remainder = 0;
}

/**
 * Get the date of a date_t
 *
 * \param date to get
 * \return date value
 */
mtime_t date_Get( const date_t *p_date )
{
    return p_date->date;
}

/**
 * Move forwards or backwards the date of a date_t.
 *
 * \param date to move
 * \param difference value
 */
void date_Move( date_t *p_date, mtime_t i_difference )
{
    p_date->date += i_difference;
}

/**
 * Increment the date and return the result, taking into account
 * rounding errors.
 *
 * \param date to increment
 * \param incrementation in number of samples
 * \return date value
 */
mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples )
{
437
438
439
440
441
    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;
442
443
444
445

    if( p_date->i_remainder >= p_date->i_divider_num )
    {
        /* This is Bresenham algorithm. */
446
        assert( p_date->i_remainder < 2*p_date->i_divider_num);
447
        p_date->date += 1;
448
449
450
451
452
        p_date->i_remainder -= p_date->i_divider_num;
    }

    return p_date->date;
}
453

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
/**
 * 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;
}

481
482
#ifndef HAVE_GETTIMEOFDAY

483
#ifdef WIN32
484

485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/*
 * 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);
}

522
#endif
523

524
#endif
525

526
527
528
529
530
531
/**
 * @return NTP 64-bits timestamp in host byte order.
 */
uint64_t NTPtime64 (void)
{
    struct timespec ts;
532
#if defined (CLOCK_REALTIME) && !defined (__OS2__)
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    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;
}