/*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio 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. PulseAudio 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #ifdef OS_IS_DARWIN #define _POSIX_C_SOURCE 1 #endif #include #include #include #include #ifdef HAVE_SYS_PRCTL_H #include #endif #ifdef OS_IS_DARWIN #include #include #include #include #endif #ifdef HAVE_WINDOWS_H #include #endif #include #include #include #include "core-rtclock.h" #ifdef OS_IS_WIN32 static int64_t counter_freq = 0; #endif pa_usec_t pa_rtclock_age(const struct timeval *tv) { struct timeval now; pa_assert(tv); return pa_timeval_diff(pa_rtclock_get(&now), tv); } struct timeval *pa_rtclock_get(struct timeval *tv) { #if defined(OS_IS_DARWIN) uint64_t val, abs_time = mach_absolute_time(); Nanoseconds nanos; nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time); val = *(uint64_t *) &nanos; tv->tv_sec = val / PA_NSEC_PER_SEC; tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC; return tv; #elif defined(HAVE_CLOCK_GETTIME) struct timespec ts; #ifdef CLOCK_MONOTONIC /* No locking or atomic ops for no_monotonic here */ static pa_bool_t no_monotonic = FALSE; if (!no_monotonic) if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) no_monotonic = TRUE; if (no_monotonic) #endif /* CLOCK_MONOTONIC */ pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); pa_assert(tv); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC; return tv; #elif defined(OS_IS_WIN32) if (counter_freq > 0) { LARGE_INTEGER count; pa_assert_se(QueryPerformanceCounter(&count)); tv->tv_sec = count.QuadPart / counter_freq; tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq; return tv; } #endif /* HAVE_CLOCK_GETTIME */ return pa_gettimeofday(tv); } pa_bool_t pa_rtclock_hrtimer(void) { #if defined (OS_IS_DARWIN) mach_timebase_info_data_t tbi; uint64_t time_nsec; mach_timebase_info(&tbi); /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */ time_nsec = tbi.numer / tbi.denom; return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); #elif defined(HAVE_CLOCK_GETTIME) struct timespec ts; #ifdef CLOCK_MONOTONIC if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); #endif /* CLOCK_MONOTONIC */ pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); #elif defined(OS_IS_WIN32) if (counter_freq > 0) return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC); #endif /* HAVE_CLOCK_GETTIME */ return FALSE; } #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC)) void pa_rtclock_hrtimer_enable(void) { #ifdef PR_SET_TIMERSLACK int slack_ns; if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) { pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported."); return; } pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); if (slack_ns > TIMER_SLACK_NS) { slack_ns = TIMER_SLACK_NS; pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) { pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno)); return; } } #elif defined(OS_IS_WIN32) LARGE_INTEGER freq; pa_assert_se(QueryPerformanceFrequency(&freq)); counter_freq = freq.QuadPart; #endif } struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) { struct timeval wc_now, rt_now; pa_assert(tv); pa_gettimeofday(&wc_now); pa_rtclock_get(&rt_now); /* pa_timeval_sub() saturates on underflow! */ if (pa_timeval_cmp(&wc_now, tv) < 0) pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now)); else pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv)); *tv = rt_now; return tv; } #ifdef HAVE_CLOCK_GETTIME pa_usec_t pa_timespec_load(const struct timespec *ts) { if (PA_UNLIKELY(!ts)) return PA_USEC_INVALID; return (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC + (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC; } struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) { pa_assert(ts); if (PA_UNLIKELY(v == PA_USEC_INVALID)) { ts->tv_sec = PA_INT_TYPE_MAX(time_t); ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1); return NULL; } ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC); ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC); return ts; } #endif static struct timeval* wallclock_from_rtclock(struct timeval *tv) { struct timeval wc_now, rt_now; pa_assert(tv); pa_gettimeofday(&wc_now); pa_rtclock_get(&rt_now); /* pa_timeval_sub() saturates on underflow! */ if (pa_timeval_cmp(&rt_now, tv) < 0) pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now)); else pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv)); *tv = wc_now; return tv; } struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock) { pa_assert(tv); if (v == PA_USEC_INVALID) return NULL; pa_timeval_store(tv, v); if (rtclock) tv->tv_usec |= PA_TIMEVAL_RTCLOCK; else wallclock_from_rtclock(tv); return tv; }