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