• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11 
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16 
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #ifdef OS_IS_DARWIN
26 #define _POSIX_C_SOURCE 1
27 #endif
28 
29 #include <stddef.h>
30 #include <time.h>
31 #include <sys/time.h>
32 #include <errno.h>
33 
34 #ifdef HAVE_SYS_PRCTL_H
35 #include <sys/prctl.h>
36 #endif
37 
38 #ifdef OS_IS_DARWIN
39 #include <CoreServices/CoreServices.h>
40 #include <mach/mach.h>
41 #include <mach/mach_time.h>
42 #include <unistd.h>
43 #endif
44 
45 #ifdef HAVE_WINDOWS_H
46 #include <windows.h>
47 #endif
48 
49 #include <pulse/timeval.h>
50 #include <pulsecore/macro.h>
51 #include <pulsecore/core-error.h>
52 
53 #include "core-rtclock.h"
54 
55 #ifdef OS_IS_WIN32
56 static int64_t counter_freq = 0;
57 #endif
58 
pa_rtclock_age(const struct timeval * tv)59 pa_usec_t pa_rtclock_age(const struct timeval *tv) {
60     struct timeval now;
61     pa_assert(tv);
62 
63     return pa_timeval_diff(pa_rtclock_get(&now), tv);
64 }
65 
pa_rtclock_get(struct timeval * tv)66 struct timeval *pa_rtclock_get(struct timeval *tv) {
67 
68 #if defined(OS_IS_DARWIN)
69     uint64_t val, abs_time = mach_absolute_time();
70     Nanoseconds nanos;
71 
72     nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
73     val = *(uint64_t *) &nanos;
74 
75     tv->tv_sec = val / PA_NSEC_PER_SEC;
76     tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
77 
78     return tv;
79 
80 #elif defined(HAVE_CLOCK_GETTIME)
81     struct timespec ts;
82 
83 #ifdef CLOCK_MONOTONIC
84     /* No locking or atomic ops for no_monotonic here */
85     static bool no_monotonic = false;
86 
87     if (!no_monotonic)
88         if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
89             no_monotonic = true;
90 
91     if (no_monotonic)
92 #endif /* CLOCK_MONOTONIC */
93         pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
94 
95     pa_assert(tv);
96 
97     tv->tv_sec = ts.tv_sec;
98     tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
99 
100     return tv;
101 #elif defined(OS_IS_WIN32)
102     if (counter_freq > 0) {
103         LARGE_INTEGER count;
104 
105         pa_assert_se(QueryPerformanceCounter(&count));
106 
107         tv->tv_sec = count.QuadPart / counter_freq;
108         tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq;
109 
110         return tv;
111     }
112 #endif /* HAVE_CLOCK_GETTIME */
113 
114     return pa_gettimeofday(tv);
115 }
116 
pa_rtclock_hrtimer(void)117 bool pa_rtclock_hrtimer(void) {
118 
119 #if defined (OS_IS_DARWIN)
120     mach_timebase_info_data_t tbi;
121     uint64_t time_nsec;
122 
123     mach_timebase_info(&tbi);
124 
125     /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
126     time_nsec = tbi.numer / tbi.denom;
127     return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
128 
129 #elif defined(HAVE_CLOCK_GETTIME)
130     struct timespec ts;
131 
132 #ifdef CLOCK_MONOTONIC
133 
134     if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
135         return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
136 
137 #endif /* CLOCK_MONOTONIC */
138 
139     pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
140     return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
141 
142 #elif defined(OS_IS_WIN32)
143 
144     if (counter_freq > 0)
145         return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC);
146 
147 #endif /* HAVE_CLOCK_GETTIME */
148 
149     return false;
150 }
151 
152 #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
153 
pa_rtclock_hrtimer_enable(void)154 void pa_rtclock_hrtimer_enable(void) {
155 
156 #ifdef PR_SET_TIMERSLACK
157     int slack_ns;
158 
159     if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
160         pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
161         return;
162     }
163 
164     pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
165 
166     if (slack_ns > TIMER_SLACK_NS) {
167         slack_ns = TIMER_SLACK_NS;
168 
169         pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
170 
171         if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
172             pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
173             return;
174         }
175     }
176 
177 #elif defined(OS_IS_WIN32)
178     LARGE_INTEGER freq;
179 
180     pa_assert_se(QueryPerformanceFrequency(&freq));
181     counter_freq = freq.QuadPart;
182 
183 #endif
184 }
185 
pa_rtclock_from_wallclock(struct timeval * tv)186 struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
187     struct timeval wc_now, rt_now;
188 
189     pa_assert(tv);
190 
191     pa_gettimeofday(&wc_now);
192     pa_rtclock_get(&rt_now);
193 
194     /* pa_timeval_sub() saturates on underflow! */
195 
196     if (pa_timeval_cmp(&wc_now, tv) < 0)
197         pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
198     else
199         pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
200 
201     *tv = rt_now;
202 
203     return tv;
204 }
205 
206 #ifdef HAVE_CLOCK_GETTIME
pa_timespec_load(const struct timespec * ts)207 pa_usec_t pa_timespec_load(const struct timespec *ts) {
208 
209     if (PA_UNLIKELY(!ts))
210         return PA_USEC_INVALID;
211 
212     return
213         (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
214         (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
215 }
216 
pa_timespec_store(struct timespec * ts,pa_usec_t v)217 struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
218     pa_assert(ts);
219 
220     if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
221         ts->tv_sec = PA_INT_TYPE_MAX(time_t);
222         ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
223         return NULL;
224     }
225 
226     ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
227     ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
228 
229     return ts;
230 }
231 #endif
232 
wallclock_from_rtclock(struct timeval * tv)233 static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
234     struct timeval wc_now, rt_now;
235 
236     pa_assert(tv);
237 
238     pa_gettimeofday(&wc_now);
239     pa_rtclock_get(&rt_now);
240 
241     /* pa_timeval_sub() saturates on underflow! */
242 
243     if (pa_timeval_cmp(&rt_now, tv) < 0)
244         pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
245     else
246         pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
247 
248     *tv = wc_now;
249 
250     return tv;
251 }
252 
pa_timeval_rtstore(struct timeval * tv,pa_usec_t v,bool rtclock)253 struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock) {
254     pa_assert(tv);
255 
256     if (v == PA_USEC_INVALID)
257         return NULL;
258 
259     pa_timeval_store(tv, v);
260 
261     if (rtclock)
262         tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
263     else
264         wallclock_from_rtclock(tv);
265 
266     return tv;
267 }
268