]> code.delx.au - pulseaudio/blob - src/pulsecore/core-rtclock.c
remap: Change remapping function argument type from void to int16_t / float as approp...
[pulseaudio] / src / pulsecore / core-rtclock.c
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #ifdef OS_IS_DARWIN
28 #define _POSIX_C_SOURCE 1
29 #endif
30
31 #include <stddef.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <errno.h>
35
36 #ifdef HAVE_SYS_PRCTL_H
37 #include <sys/prctl.h>
38 #endif
39
40 #ifdef OS_IS_DARWIN
41 #include <CoreServices/CoreServices.h>
42 #include <mach/mach.h>
43 #include <mach/mach_time.h>
44 #include <unistd.h>
45 #endif
46
47 #ifdef HAVE_WINDOWS_H
48 #include <windows.h>
49 #endif
50
51 #include <pulse/timeval.h>
52 #include <pulsecore/macro.h>
53 #include <pulsecore/core-error.h>
54
55 #include "core-rtclock.h"
56
57 #ifdef OS_IS_WIN32
58 static int64_t counter_freq = 0;
59 #endif
60
61 pa_usec_t pa_rtclock_age(const struct timeval *tv) {
62 struct timeval now;
63 pa_assert(tv);
64
65 return pa_timeval_diff(pa_rtclock_get(&now), tv);
66 }
67
68 struct timeval *pa_rtclock_get(struct timeval *tv) {
69
70 #if defined(OS_IS_DARWIN)
71 uint64_t val, abs_time = mach_absolute_time();
72 Nanoseconds nanos;
73
74 nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
75 val = *(uint64_t *) &nanos;
76
77 tv->tv_sec = val / PA_NSEC_PER_SEC;
78 tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
79
80 return tv;
81
82 #elif defined(HAVE_CLOCK_GETTIME)
83 struct timespec ts;
84
85 #ifdef CLOCK_MONOTONIC
86 /* No locking or atomic ops for no_monotonic here */
87 static bool no_monotonic = false;
88
89 if (!no_monotonic)
90 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
91 no_monotonic = true;
92
93 if (no_monotonic)
94 #endif /* CLOCK_MONOTONIC */
95 pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
96
97 pa_assert(tv);
98
99 tv->tv_sec = ts.tv_sec;
100 tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
101
102 return tv;
103 #elif defined(OS_IS_WIN32)
104 if (counter_freq > 0) {
105 LARGE_INTEGER count;
106
107 pa_assert_se(QueryPerformanceCounter(&count));
108
109 tv->tv_sec = count.QuadPart / counter_freq;
110 tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq;
111
112 return tv;
113 }
114 #endif /* HAVE_CLOCK_GETTIME */
115
116 return pa_gettimeofday(tv);
117 }
118
119 bool pa_rtclock_hrtimer(void) {
120
121 #if defined (OS_IS_DARWIN)
122 mach_timebase_info_data_t tbi;
123 uint64_t time_nsec;
124
125 mach_timebase_info(&tbi);
126
127 /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
128 time_nsec = tbi.numer / tbi.denom;
129 return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
130
131 #elif defined(HAVE_CLOCK_GETTIME)
132 struct timespec ts;
133
134 #ifdef CLOCK_MONOTONIC
135
136 if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
137 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
138
139 #endif /* CLOCK_MONOTONIC */
140
141 pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
142 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
143
144 #elif defined(OS_IS_WIN32)
145
146 if (counter_freq > 0)
147 return counter_freq >= (int64_t) (PA_USEC_PER_SEC/PA_HRTIMER_THRESHOLD_USEC);
148
149 #endif /* HAVE_CLOCK_GETTIME */
150
151 return false;
152 }
153
154 #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
155
156 void pa_rtclock_hrtimer_enable(void) {
157
158 #ifdef PR_SET_TIMERSLACK
159 int slack_ns;
160
161 if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
162 pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
163 return;
164 }
165
166 pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
167
168 if (slack_ns > TIMER_SLACK_NS) {
169 slack_ns = TIMER_SLACK_NS;
170
171 pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
172
173 if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
174 pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
175 return;
176 }
177 }
178
179 #elif defined(OS_IS_WIN32)
180 LARGE_INTEGER freq;
181
182 pa_assert_se(QueryPerformanceFrequency(&freq));
183 counter_freq = freq.QuadPart;
184
185 #endif
186 }
187
188 struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
189 struct timeval wc_now, rt_now;
190
191 pa_assert(tv);
192
193 pa_gettimeofday(&wc_now);
194 pa_rtclock_get(&rt_now);
195
196 /* pa_timeval_sub() saturates on underflow! */
197
198 if (pa_timeval_cmp(&wc_now, tv) < 0)
199 pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
200 else
201 pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
202
203 *tv = rt_now;
204
205 return tv;
206 }
207
208 #ifdef HAVE_CLOCK_GETTIME
209 pa_usec_t pa_timespec_load(const struct timespec *ts) {
210
211 if (PA_UNLIKELY(!ts))
212 return PA_USEC_INVALID;
213
214 return
215 (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
216 (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
217 }
218
219 struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
220 pa_assert(ts);
221
222 if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
223 ts->tv_sec = PA_INT_TYPE_MAX(time_t);
224 ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
225 return NULL;
226 }
227
228 ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
229 ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
230
231 return ts;
232 }
233 #endif
234
235 static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
236 struct timeval wc_now, rt_now;
237
238 pa_assert(tv);
239
240 pa_gettimeofday(&wc_now);
241 pa_rtclock_get(&rt_now);
242
243 /* pa_timeval_sub() saturates on underflow! */
244
245 if (pa_timeval_cmp(&rt_now, tv) < 0)
246 pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
247 else
248 pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
249
250 *tv = wc_now;
251
252 return tv;
253 }
254
255 struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, bool rtclock) {
256 pa_assert(tv);
257
258 if (v == PA_USEC_INVALID)
259 return NULL;
260
261 pa_timeval_store(tv, v);
262
263 if (rtclock)
264 tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
265 else
266 wallclock_from_rtclock(tv);
267
268 return tv;
269 }