merge glitch-free branch back into trunk
[profile/ivi/pulseaudio.git] / 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 }