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