]> code.delx.au - pulseaudio/blob - src/pulsecore/core-rtclock.c
Wrap clock_gettime and friends
[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 #include <stddef.h>
28 #include <time.h>
29 #include <sys/time.h>
30 #include <errno.h>
31
32 #ifdef HAVE_SYS_PRCTL_H
33 #include <sys/prctl.h>
34 #endif
35
36 #ifdef OS_IS_DARWIN
37 #include <CoreServices/CoreServices.h>
38 #include <mach/mach.h>
39 #include <mach/mach_time.h>
40 #endif
41
42 #include <pulse/timeval.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-error.h>
45
46 #include "core-rtclock.h"
47
48 pa_usec_t pa_rtclock_age(const struct timeval *tv) {
49 struct timeval now;
50 pa_assert(tv);
51
52 return pa_timeval_diff(pa_rtclock_get(&now), tv);
53 }
54
55 struct timeval *pa_rtclock_get(struct timeval *tv) {
56
57 #if defined(HAVE_CLOCK_GETTIME)
58 struct timespec ts;
59
60 #ifdef CLOCK_MONOTONIC
61 /* No locking or atomic ops for no_monotonic here */
62 static pa_bool_t no_monotonic = FALSE;
63
64 if (!no_monotonic)
65 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
66 no_monotonic = TRUE;
67
68 if (no_monotonic)
69 #endif /* CLOCK_MONOTONIC */
70 pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
71
72 pa_assert(tv);
73
74 tv->tv_sec = ts.tv_sec;
75 tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
76
77 return tv;
78
79 #elif defined(OS_IS_DARWIN)
80 static mach_timebase_info_data_t tbi;
81 uint64_t nticks;
82 uint64_t time_nsec;
83
84 /* Refer Apple ADC QA1398
85 Also: http://devworld.apple.com/documentation/Darwin/Conceptual/KernelProgramming/services/services.html
86
87 Note: argument is timespec NOT timeval (timespec uses nsec, timeval uses usec)
88 */
89
90 /* try and be a mite efficient - maybe I should keep the N/D as a float !? */
91 if (tbi.denom == 0)
92 mach_timebase_info(&tbi);
93
94 nticks = mach_absolute_time();
95 time_nsec = nticks * tbi.numer / tbi.denom; // see above
96
97 tv->tv_sec = time_nsec / PA_NSEC_PER_SEC;
98 tv->tv_usec = time_nsec / PA_NSEC_PER_USEC;
99
100 return tv;
101
102 #else /* OS_IS_DARWIN */
103
104 return pa_gettimeofday(tv);
105
106 #endif
107 }
108
109 pa_bool_t pa_rtclock_hrtimer(void) {
110
111 #if defined(HAVE_CLOCK_GETTIME)
112 struct timespec ts;
113
114 #ifdef CLOCK_MONOTONIC
115
116 if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
117 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
118 #endif /* CLOCK_MONOTONIC */
119
120 pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
121 return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
122
123 #elif defined (OS_IS_DARWIN)
124 mach_timebase_info_data_t tbi;
125 uint64_t time_nsec;
126
127 mach_timebase_info(&tbi);
128
129 /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
130 time_nsec = tbi.numer / tbi.denom;
131 return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
132
133 #else /* OS_IS_DARWIN */
134 return FALSE;
135
136 #endif
137 }
138
139 #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
140
141 void pa_rtclock_hrtimer_enable(void) {
142
143 #ifdef PR_SET_TIMERSLACK
144 int slack_ns;
145
146 if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
147 pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
148 return;
149 }
150
151 pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
152
153 if (slack_ns > TIMER_SLACK_NS) {
154 slack_ns = TIMER_SLACK_NS;
155
156 pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
157
158 if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
159 pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
160 return;
161 }
162 }
163
164 #endif
165 }
166
167 struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
168
169 #ifdef HAVE_CLOCK_GETTIME
170 struct timeval wc_now, rt_now;
171
172 pa_gettimeofday(&wc_now);
173 pa_rtclock_get(&rt_now);
174
175 pa_assert(tv);
176
177 /* pa_timeval_sub() saturates on underflow! */
178
179 if (pa_timeval_cmp(&wc_now, tv) < 0)
180 pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
181 else
182 pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
183
184 *tv = rt_now;
185 #endif
186
187 return tv;
188 }
189
190 pa_usec_t pa_timespec_load(const struct timespec *ts) {
191
192 if (PA_UNLIKELY(!ts))
193 return PA_USEC_INVALID;
194
195 return
196 (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
197 (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
198 }
199
200 struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
201 pa_assert(ts);
202
203 if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
204 ts->tv_sec = PA_INT_TYPE_MAX(time_t);
205 ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
206 return NULL;
207 }
208
209 ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
210 ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
211
212 return ts;
213 }
214
215 static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
216
217 #ifdef HAVE_CLOCK_GETTIME
218 struct timeval wc_now, rt_now;
219
220 pa_gettimeofday(&wc_now);
221 pa_rtclock_get(&rt_now);
222
223 pa_assert(tv);
224
225 /* pa_timeval_sub() saturates on underflow! */
226
227 if (pa_timeval_cmp(&rt_now, tv) < 0)
228 pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
229 else
230 pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
231
232 *tv = wc_now;
233 #endif
234
235 return tv;
236 }
237
238 struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock) {
239 pa_assert(tv);
240
241 if (v == PA_USEC_INVALID)
242 return NULL;
243
244 pa_timeval_store(tv, v);
245
246 if (rtclock)
247 tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
248 else
249 wallclock_from_rtclock(tv);
250
251 return tv;
252 }