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(HAVE_CLOCK_GETTIME)
69 struct timespec ts;
70
71 #ifdef CLOCK_MONOTONIC
72 /* No locking or atomic ops for no_monotonic here */
73 static bool no_monotonic = false;
74
75 if (!no_monotonic)
76 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
77 no_monotonic = true;
78
79 if (no_monotonic)
80 #endif /* CLOCK_MONOTONIC */
81 pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
82
83 pa_assert(tv);
84
85 tv->tv_sec = ts.tv_sec;
86 tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
87
88 return tv;
89 #elif defined(OS_IS_DARWIN)
90 uint64_t val, abs_time = mach_absolute_time();
91 Nanoseconds nanos;
92
93 nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
94 val = *(uint64_t *) &nanos;
95
96 tv->tv_sec = val / PA_NSEC_PER_SEC;
97 tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
98
99 return tv;
100 #elif defined(OS_IS_WIN32)
101 if (counter_freq > 0) {
102 LARGE_INTEGER count;
103
104 pa_assert_se(QueryPerformanceCounter(&count));
105
106 tv->tv_sec = count.QuadPart / counter_freq;
107 tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq;
108
109 return tv;
110 }
111 #endif /* HAVE_CLOCK_GETTIME */
112
113 return pa_gettimeofday(tv);
114 }
115
pa_rtclock_hrtimer(void)116 bool pa_rtclock_hrtimer(void) {
117
118 #if defined (OS_IS_DARWIN)
119 mach_timebase_info_data_t tbi;
120 uint64_t time_nsec;
121
122 mach_timebase_info(&tbi);
123
124 /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
125 time_nsec = tbi.numer / tbi.denom;
126 return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
127
128 #elif defined(HAVE_CLOCK_GETTIME)
129 struct timespec ts;
130
131 #ifdef CLOCK_MONOTONIC
132
133 if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
134 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
135
136 #endif /* CLOCK_MONOTONIC */
137
138 pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
139 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
140
141 #elif defined(OS_IS_WIN32)
142
143 if (counter_freq > 0)
144 return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC);
145
146 #endif /* HAVE_CLOCK_GETTIME */
147
148 return false;
149 }
150
151 #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
152
pa_rtclock_hrtimer_enable(void)153 void pa_rtclock_hrtimer_enable(void) {
154
155 #ifdef PR_SET_TIMERSLACK
156 int slack_ns;
157
158 if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
159 pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
160 return;
161 }
162
163 pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
164
165 if (slack_ns > TIMER_SLACK_NS) {
166 slack_ns = TIMER_SLACK_NS;
167
168 pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
169
170 if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
171 pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
172 return;
173 }
174 }
175
176 #elif defined(OS_IS_WIN32)
177 LARGE_INTEGER freq;
178
179 pa_assert_se(QueryPerformanceFrequency(&freq));
180 counter_freq = freq.QuadPart;
181
182 #endif
183 }
184
pa_rtclock_from_wallclock(struct timeval * tv)185 struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
186 struct timeval wc_now, rt_now;
187
188 pa_assert(tv);
189
190 pa_gettimeofday(&wc_now);
191 pa_rtclock_get(&rt_now);
192
193 /* pa_timeval_sub() saturates on underflow! */
194
195 if (pa_timeval_cmp(&wc_now, tv) < 0)
196 pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
197 else
198 pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
199
200 *tv = rt_now;
201
202 return tv;
203 }
204
205 #ifdef HAVE_CLOCK_GETTIME
pa_timespec_load(const struct timespec * ts)206 pa_usec_t pa_timespec_load(const struct timespec *ts) {
207
208 if (PA_UNLIKELY(!ts))
209 return PA_USEC_INVALID;
210
211 return
212 (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
213 (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
214 }
215
pa_timespec_store(struct timespec * ts,pa_usec_t v)216 struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
217 pa_assert(ts);
218
219 if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
220 ts->tv_sec = PA_INT_TYPE_MAX(time_t);
221 ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
222 return NULL;
223 }
224
225 ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
226 ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
227
228 return ts;
229 }
230 #endif
231
wallclock_from_rtclock(struct timeval * tv)232 static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
233 struct timeval wc_now, rt_now;
234
235 pa_assert(tv);
236
237 pa_gettimeofday(&wc_now);
238 pa_rtclock_get(&rt_now);
239
240 /* pa_timeval_sub() saturates on underflow! */
241
242 if (pa_timeval_cmp(&rt_now, tv) < 0)
243 pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
244 else
245 pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
246
247 *tv = wc_now;
248
249 return tv;
250 }
251
pa_timeval_rtstore(struct timeval * tv,pa_usec_t v,bool rtclock)252 struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock) {
253 pa_assert(tv);
254
255 if (v == PA_USEC_INVALID)
256 return NULL;
257
258 pa_timeval_store(tv, v);
259
260 if (rtclock)
261 tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
262 else
263 wallclock_from_rtclock(tv);
264
265 return tv;
266 }