]> code.delx.au - pulseaudio/blob - src/pulsecore/fdsem.c
merge 'lennart' 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(SYS_eventfd) && defined(__NR_eventfd)
57 #define SYS_eventfd __NR_eventfd
58 #endif
59
60 #ifdef SYS_eventfd
61 #define HAVE_EVENTFD
62
63 static inline long eventfd(unsigned count) {
64 return syscall(SYS_eventfd, count);
65 }
66
67 #endif
68 #endif
69
70 #include "fdsem.h"
71
72 struct pa_fdsem {
73 int fds[2];
74 #ifdef HAVE_EVENTFD
75 int efd;
76 #endif
77 pa_atomic_t waiting;
78 pa_atomic_t signalled;
79 pa_atomic_t in_pipe;
80 };
81
82 pa_fdsem *pa_fdsem_new(void) {
83 pa_fdsem *f;
84
85 f = pa_xnew(pa_fdsem, 1);
86
87 #ifdef HAVE_EVENTFD
88 if ((f->efd = eventfd(0)) >= 0) {
89 pa_make_fd_cloexec(f->efd);
90 f->fds[0] = f->fds[1] = -1;
91
92 } else
93 #endif
94 {
95 if (pipe(f->fds) < 0) {
96 pa_xfree(f);
97 return NULL;
98 }
99
100 pa_make_fd_cloexec(f->fds[0]);
101 pa_make_fd_cloexec(f->fds[1]);
102 }
103
104 pa_atomic_store(&f->waiting, 0);
105 pa_atomic_store(&f->signalled, 0);
106 pa_atomic_store(&f->in_pipe, 0);
107
108 return f;
109 }
110
111 void pa_fdsem_free(pa_fdsem *f) {
112 pa_assert(f);
113
114 #ifdef HAVE_EVENTFD
115 if (f->efd >= 0)
116 pa_close(f->efd);
117 #endif
118 pa_close_pipe(f->fds);
119
120 pa_xfree(f);
121 }
122
123 static void flush(pa_fdsem *f) {
124 ssize_t r;
125 pa_assert(f);
126
127 if (pa_atomic_load(&f->in_pipe) <= 0)
128 return;
129
130 do {
131 char x[10];
132
133 #ifdef HAVE_EVENTFD
134 if (f->efd >= 0) {
135 uint64_t u;
136
137 if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
138 pa_assert(r < 0 && errno == EINTR);
139 continue;
140 }
141 r = (ssize_t) u;
142 } else
143 #endif
144
145 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
146 pa_assert(r < 0 && errno == EINTR);
147 continue;
148 }
149
150 } while (pa_atomic_sub(&f->in_pipe, r) > r);
151 }
152
153 void pa_fdsem_post(pa_fdsem *f) {
154 pa_assert(f);
155
156 if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) {
157
158 if (pa_atomic_load(&f->waiting)) {
159 ssize_t r;
160 char x = 'x';
161
162 pa_atomic_inc(&f->in_pipe);
163
164 for (;;) {
165
166 #ifdef HAVE_EVENTFD
167 if (f->efd >= 0) {
168 uint64_t u = 1;
169
170 if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) {
171 pa_assert(r < 0 && errno == EINTR);
172 continue;
173 }
174 } else
175 #endif
176
177 if ((r = write(f->fds[1], &x, 1)) != 1) {
178 pa_assert(r < 0 && errno == EINTR);
179 continue;
180 }
181
182 break;
183 }
184 }
185 }
186 }
187
188 void pa_fdsem_wait(pa_fdsem *f) {
189 pa_assert(f);
190
191 flush(f);
192
193 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
194 return;
195
196 pa_atomic_inc(&f->waiting);
197
198 while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
199 char x[10];
200 ssize_t r;
201
202 #ifdef HAVE_EVENTFD
203 if (f->efd >= 0) {
204 uint64_t u;
205
206 if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
207 pa_assert(r < 0 && errno == EINTR);
208 continue;
209 }
210
211 r = (ssize_t) u;
212 } else
213 #endif
214
215 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
216 pa_assert(r < 0 && errno == EINTR);
217 continue;
218 }
219
220 pa_atomic_sub(&f->in_pipe, r);
221 }
222
223 pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
224 }
225
226 int pa_fdsem_try(pa_fdsem *f) {
227 pa_assert(f);
228
229 flush(f);
230
231 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
232 return 1;
233
234 return 0;
235 }
236
237 int pa_fdsem_get(pa_fdsem *f) {
238 pa_assert(f);
239
240 #ifdef HAVE_EVENTFD
241 if (f->efd >= 0)
242 return f->efd;
243 #endif
244
245 return f->fds[0];
246 }
247
248 int pa_fdsem_before_poll(pa_fdsem *f) {
249 pa_assert(f);
250
251 flush(f);
252
253 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
254 return -1;
255
256 pa_atomic_inc(&f->waiting);
257
258 if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
259 pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
260 return -1;
261 }
262 return 0;
263 }
264
265 int pa_fdsem_after_poll(pa_fdsem *f) {
266 pa_assert(f);
267
268 pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
269
270 flush(f);
271
272 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
273 return 1;
274
275 return 0;
276 }