• 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(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 }