153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
953a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
1053a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  Lesser General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1853a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#ifdef OS_IS_DARWIN
2653a5a1b3Sopenharmony_ci#define _POSIX_C_SOURCE 1
2753a5a1b3Sopenharmony_ci#endif
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <stddef.h>
3053a5a1b3Sopenharmony_ci#include <time.h>
3153a5a1b3Sopenharmony_ci#include <sys/time.h>
3253a5a1b3Sopenharmony_ci#include <errno.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_PRCTL_H
3553a5a1b3Sopenharmony_ci#include <sys/prctl.h>
3653a5a1b3Sopenharmony_ci#endif
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#ifdef OS_IS_DARWIN
3953a5a1b3Sopenharmony_ci#include <CoreServices/CoreServices.h>
4053a5a1b3Sopenharmony_ci#include <mach/mach.h>
4153a5a1b3Sopenharmony_ci#include <mach/mach_time.h>
4253a5a1b3Sopenharmony_ci#include <unistd.h>
4353a5a1b3Sopenharmony_ci#endif
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_ci#ifdef HAVE_WINDOWS_H
4653a5a1b3Sopenharmony_ci#include <windows.h>
4753a5a1b3Sopenharmony_ci#endif
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
5053a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
5153a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5253a5a1b3Sopenharmony_ci
5353a5a1b3Sopenharmony_ci#include "core-rtclock.h"
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci#ifdef OS_IS_WIN32
5653a5a1b3Sopenharmony_cistatic int64_t counter_freq = 0;
5753a5a1b3Sopenharmony_ci#endif
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_cipa_usec_t pa_rtclock_age(const struct timeval *tv) {
6053a5a1b3Sopenharmony_ci    struct timeval now;
6153a5a1b3Sopenharmony_ci    pa_assert(tv);
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci    return pa_timeval_diff(pa_rtclock_get(&now), tv);
6453a5a1b3Sopenharmony_ci}
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_cistruct timeval *pa_rtclock_get(struct timeval *tv) {
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci#if defined(OS_IS_DARWIN)
6953a5a1b3Sopenharmony_ci    uint64_t val, abs_time = mach_absolute_time();
7053a5a1b3Sopenharmony_ci    Nanoseconds nanos;
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci    nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
7353a5a1b3Sopenharmony_ci    val = *(uint64_t *) &nanos;
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci    tv->tv_sec = val / PA_NSEC_PER_SEC;
7653a5a1b3Sopenharmony_ci    tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci    return tv;
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci#elif defined(HAVE_CLOCK_GETTIME)
8153a5a1b3Sopenharmony_ci    struct timespec ts;
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci#ifdef CLOCK_MONOTONIC
8453a5a1b3Sopenharmony_ci    /* No locking or atomic ops for no_monotonic here */
8553a5a1b3Sopenharmony_ci    static bool no_monotonic = false;
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci    if (!no_monotonic)
8853a5a1b3Sopenharmony_ci        if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
8953a5a1b3Sopenharmony_ci            no_monotonic = true;
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci    if (no_monotonic)
9253a5a1b3Sopenharmony_ci#endif /* CLOCK_MONOTONIC */
9353a5a1b3Sopenharmony_ci        pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
9453a5a1b3Sopenharmony_ci
9553a5a1b3Sopenharmony_ci    pa_assert(tv);
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    tv->tv_sec = ts.tv_sec;
9853a5a1b3Sopenharmony_ci    tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    return tv;
10153a5a1b3Sopenharmony_ci#elif defined(OS_IS_WIN32)
10253a5a1b3Sopenharmony_ci    if (counter_freq > 0) {
10353a5a1b3Sopenharmony_ci        LARGE_INTEGER count;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci        pa_assert_se(QueryPerformanceCounter(&count));
10653a5a1b3Sopenharmony_ci
10753a5a1b3Sopenharmony_ci        tv->tv_sec = count.QuadPart / counter_freq;
10853a5a1b3Sopenharmony_ci        tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq;
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci        return tv;
11153a5a1b3Sopenharmony_ci    }
11253a5a1b3Sopenharmony_ci#endif /* HAVE_CLOCK_GETTIME */
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci    return pa_gettimeofday(tv);
11553a5a1b3Sopenharmony_ci}
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_cibool pa_rtclock_hrtimer(void) {
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_ci#if defined (OS_IS_DARWIN)
12053a5a1b3Sopenharmony_ci    mach_timebase_info_data_t tbi;
12153a5a1b3Sopenharmony_ci    uint64_t time_nsec;
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci    mach_timebase_info(&tbi);
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
12653a5a1b3Sopenharmony_ci    time_nsec = tbi.numer / tbi.denom;
12753a5a1b3Sopenharmony_ci    return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci#elif defined(HAVE_CLOCK_GETTIME)
13053a5a1b3Sopenharmony_ci    struct timespec ts;
13153a5a1b3Sopenharmony_ci
13253a5a1b3Sopenharmony_ci#ifdef CLOCK_MONOTONIC
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
13553a5a1b3Sopenharmony_ci        return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci#endif /* CLOCK_MONOTONIC */
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
14053a5a1b3Sopenharmony_ci    return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci#elif defined(OS_IS_WIN32)
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ci    if (counter_freq > 0)
14553a5a1b3Sopenharmony_ci        return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC);
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_ci#endif /* HAVE_CLOCK_GETTIME */
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    return false;
15053a5a1b3Sopenharmony_ci}
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_civoid pa_rtclock_hrtimer_enable(void) {
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci#ifdef PR_SET_TIMERSLACK
15753a5a1b3Sopenharmony_ci    int slack_ns;
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci    if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
16053a5a1b3Sopenharmony_ci        pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
16153a5a1b3Sopenharmony_ci        return;
16253a5a1b3Sopenharmony_ci    }
16353a5a1b3Sopenharmony_ci
16453a5a1b3Sopenharmony_ci    pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    if (slack_ns > TIMER_SLACK_NS) {
16753a5a1b3Sopenharmony_ci        slack_ns = TIMER_SLACK_NS;
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci        pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_ci        if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
17253a5a1b3Sopenharmony_ci            pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
17353a5a1b3Sopenharmony_ci            return;
17453a5a1b3Sopenharmony_ci        }
17553a5a1b3Sopenharmony_ci    }
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci#elif defined(OS_IS_WIN32)
17853a5a1b3Sopenharmony_ci    LARGE_INTEGER freq;
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci    pa_assert_se(QueryPerformanceFrequency(&freq));
18153a5a1b3Sopenharmony_ci    counter_freq = freq.QuadPart;
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci#endif
18453a5a1b3Sopenharmony_ci}
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_cistruct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
18753a5a1b3Sopenharmony_ci    struct timeval wc_now, rt_now;
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci    pa_assert(tv);
19053a5a1b3Sopenharmony_ci
19153a5a1b3Sopenharmony_ci    pa_gettimeofday(&wc_now);
19253a5a1b3Sopenharmony_ci    pa_rtclock_get(&rt_now);
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci    /* pa_timeval_sub() saturates on underflow! */
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci    if (pa_timeval_cmp(&wc_now, tv) < 0)
19753a5a1b3Sopenharmony_ci        pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
19853a5a1b3Sopenharmony_ci    else
19953a5a1b3Sopenharmony_ci        pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci    *tv = rt_now;
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    return tv;
20453a5a1b3Sopenharmony_ci}
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci#ifdef HAVE_CLOCK_GETTIME
20753a5a1b3Sopenharmony_cipa_usec_t pa_timespec_load(const struct timespec *ts) {
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    if (PA_UNLIKELY(!ts))
21053a5a1b3Sopenharmony_ci        return PA_USEC_INVALID;
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci    return
21353a5a1b3Sopenharmony_ci        (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
21453a5a1b3Sopenharmony_ci        (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
21553a5a1b3Sopenharmony_ci}
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_cistruct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
21853a5a1b3Sopenharmony_ci    pa_assert(ts);
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci    if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
22153a5a1b3Sopenharmony_ci        ts->tv_sec = PA_INT_TYPE_MAX(time_t);
22253a5a1b3Sopenharmony_ci        ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
22353a5a1b3Sopenharmony_ci        return NULL;
22453a5a1b3Sopenharmony_ci    }
22553a5a1b3Sopenharmony_ci
22653a5a1b3Sopenharmony_ci    ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
22753a5a1b3Sopenharmony_ci    ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
22853a5a1b3Sopenharmony_ci
22953a5a1b3Sopenharmony_ci    return ts;
23053a5a1b3Sopenharmony_ci}
23153a5a1b3Sopenharmony_ci#endif
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_cistatic struct timeval* wallclock_from_rtclock(struct timeval *tv) {
23453a5a1b3Sopenharmony_ci    struct timeval wc_now, rt_now;
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci    pa_assert(tv);
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci    pa_gettimeofday(&wc_now);
23953a5a1b3Sopenharmony_ci    pa_rtclock_get(&rt_now);
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    /* pa_timeval_sub() saturates on underflow! */
24253a5a1b3Sopenharmony_ci
24353a5a1b3Sopenharmony_ci    if (pa_timeval_cmp(&rt_now, tv) < 0)
24453a5a1b3Sopenharmony_ci        pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
24553a5a1b3Sopenharmony_ci    else
24653a5a1b3Sopenharmony_ci        pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
24753a5a1b3Sopenharmony_ci
24853a5a1b3Sopenharmony_ci    *tv = wc_now;
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci    return tv;
25153a5a1b3Sopenharmony_ci}
25253a5a1b3Sopenharmony_ci
25353a5a1b3Sopenharmony_cistruct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock) {
25453a5a1b3Sopenharmony_ci    pa_assert(tv);
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    if (v == PA_USEC_INVALID)
25753a5a1b3Sopenharmony_ci        return NULL;
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci    pa_timeval_store(tv, v);
26053a5a1b3Sopenharmony_ci
26153a5a1b3Sopenharmony_ci    if (rtclock)
26253a5a1b3Sopenharmony_ci        tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
26353a5a1b3Sopenharmony_ci    else
26453a5a1b3Sopenharmony_ci        wallclock_from_rtclock(tv);
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_ci    return tv;
26753a5a1b3Sopenharmony_ci}
268