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