]> code.delx.au - pulseaudio/blob - src/pulsecore/fdsem.c
merge glitch-free branch back into trunk
[pulseaudio] / src / pulsecore / fdsem.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2.1 of the
11 License, or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #ifdef HAVE_SYS_SYSCALL_H
29 #include <sys/syscall.h>
30 #endif
31
32 #include <unistd.h>
33 #include <errno.h>
34
35 #include <pulsecore/atomic.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/thread.h>
38 #include <pulsecore/macro.h>
39 #include <pulsecore/core-util.h>
40 #include <pulse/xmalloc.h>
41
42 #ifndef HAVE_PIPE
43 #include <pulsecore/pipe.h>
44 #endif
45
46 #ifdef __linux__
47
48 #if !defined(__NR_eventfd) && defined(__i386__)
49 #define __NR_eventfd 323
50 #endif
51
52 #if !defined(__NR_eventfd) && defined(__x86_64__)
53 #define __NR_eventfd 284
54 #endif
55
56 #if !defined(__NR_eventfd) && defined(__arm__)
57 #define __NR_eventfd (__NR_SYSCALL_BASE+351)
58 #endif
59
60 #if !defined(SYS_eventfd) && defined(__NR_eventfd)
61 #define SYS_eventfd __NR_eventfd
62 #endif
63
64 #ifdef SYS_eventfd
65 #define HAVE_EVENTFD
66
67 static inline long eventfd(unsigned count) {
68 return syscall(SYS_eventfd, count);
69 }
70
71 #endif
72 #endif
73
74 #include "fdsem.h"
75
76 struct pa_fdsem {
77 int fds[2];
78 #ifdef HAVE_EVENTFD
79 int efd;
80 #endif
81
82 pa_fdsem_data *data;
83 };
84
85 pa_fdsem *pa_fdsem_new(void) {
86 pa_fdsem *f;
87
88 f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
89
90 #ifdef HAVE_EVENTFD
91 if ((f->efd = eventfd(0)) >= 0) {
92 pa_make_fd_cloexec(f->efd);
93 f->fds[0] = f->fds[1] = -1;
94 } else
95 #endif
96 {
97 if (pipe(f->fds) < 0) {
98 pa_xfree(f);
99 return NULL;
100 }
101
102 pa_make_fd_cloexec(f->fds[0]);
103 pa_make_fd_cloexec(f->fds[1]);
104 }
105
106 f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
107
108 pa_atomic_store(&f->data->waiting, 0);
109 pa_atomic_store(&f->data->signalled, 0);
110 pa_atomic_store(&f->data->in_pipe, 0);
111
112 return f;
113 }
114
115 pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
116 pa_fdsem *f = NULL;
117
118 pa_assert(data);
119 pa_assert(event_fd >= 0);
120
121 #ifdef HAVE_EVENTFD
122 f = pa_xnew(pa_fdsem, 1);
123
124 f->efd = event_fd;
125 pa_make_fd_cloexec(f->efd);
126 f->fds[0] = f->fds[1] = -1;
127 f->data = data;
128 #endif
129
130 return f;
131 }
132
133 pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
134 pa_fdsem *f = NULL;
135
136 pa_assert(data);
137 pa_assert(event_fd);
138
139 #ifdef HAVE_EVENTFD
140
141 f = pa_xnew(pa_fdsem, 1);
142
143 if ((f->efd = eventfd(0)) < 0) {
144 pa_xfree(f);
145 return NULL;
146 }
147
148 pa_make_fd_cloexec(f->efd);
149 f->fds[0] = f->fds[1] = -1;
150 f->data = data;
151
152 pa_atomic_store(&f->data->waiting, 0);
153 pa_atomic_store(&f->data->signalled, 0);
154 pa_atomic_store(&f->data->in_pipe, 0);
155
156 #endif
157
158 return f;
159 }
160
161 void pa_fdsem_free(pa_fdsem *f) {
162 pa_assert(f);
163
164 #ifdef HAVE_EVENTFD
165 if (f->efd >= 0)
166 pa_close(f->efd);
167 #endif
168 pa_close_pipe(f->fds);
169
170 pa_xfree(f);
171 }
172
173 static void flush(pa_fdsem *f) {
174 ssize_t r;
175 pa_assert(f);
176
177 if (pa_atomic_load(&f->data->in_pipe) <= 0)
178 return;
179
180 do {
181 char x[10];
182
183 #ifdef HAVE_EVENTFD
184 if (f->efd >= 0) {
185 uint64_t u;
186
187 if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
188 pa_assert(r < 0 && errno == EINTR);
189 continue;
190 }
191 r = (ssize_t) u;
192 } else
193 #endif
194
195 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
196 pa_assert(r < 0 && errno == EINTR);
197 continue;
198 }
199
200 } while (pa_atomic_sub(&f->data->in_pipe, r) > r);
201 }
202
203 void pa_fdsem_post(pa_fdsem *f) {
204 pa_assert(f);
205
206 if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
207
208 if (pa_atomic_load(&f->data->waiting)) {
209 ssize_t r;
210 char x = 'x';
211
212 pa_atomic_inc(&f->data->in_pipe);
213
214 for (;;) {
215
216 #ifdef HAVE_EVENTFD
217 if (f->efd >= 0) {
218 uint64_t u = 1;
219
220 if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) {
221 pa_assert(r < 0 && errno == EINTR);
222 continue;
223 }
224 } else
225 #endif
226
227 if ((r = write(f->fds[1], &x, 1)) != 1) {
228 pa_assert(r < 0 && errno == EINTR);
229 continue;
230 }
231
232 break;
233 }
234 }
235 }
236 }
237
238 void pa_fdsem_wait(pa_fdsem *f) {
239 pa_assert(f);
240
241 flush(f);
242
243 if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
244 return;
245
246 pa_atomic_inc(&f->data->waiting);
247
248 while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
249 char x[10];
250 ssize_t r;
251
252 #ifdef HAVE_EVENTFD
253 if (f->efd >= 0) {
254 uint64_t u;
255
256 if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
257 pa_assert(r < 0 && errno == EINTR);
258 continue;
259 }
260
261 r = (ssize_t) u;
262 } else
263 #endif
264
265 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
266 pa_assert(r < 0 && errno == EINTR);
267 continue;
268 }
269
270 pa_atomic_sub(&f->data->in_pipe, r);
271 }
272
273 pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
274 }
275
276 int pa_fdsem_try(pa_fdsem *f) {
277 pa_assert(f);
278
279 flush(f);
280
281 if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
282 return 1;
283
284 return 0;
285 }
286
287 int pa_fdsem_get(pa_fdsem *f) {
288 pa_assert(f);
289
290 #ifdef HAVE_EVENTFD
291 if (f->efd >= 0)
292 return f->efd;
293 #endif
294
295 return f->fds[0];
296 }
297
298 int pa_fdsem_before_poll(pa_fdsem *f) {
299 pa_assert(f);
300
301 flush(f);
302
303 if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
304 return -1;
305
306 pa_atomic_inc(&f->data->waiting);
307
308 if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
309 pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
310 return -1;
311 }
312 return 0;
313 }
314
315 int pa_fdsem_after_poll(pa_fdsem *f) {
316 pa_assert(f);
317
318 pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
319
320 flush(f);
321
322 if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
323 return 1;
324
325 return 0;
326 }