]> code.delx.au - pulseaudio/blob - src/pulsecore/fdsem.c
allow destruction of pa_fdsem object that are still in 'poll' state
[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 <unistd.h>
29 #include <errno.h>
30
31 #include <pulsecore/atomic.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/thread.h>
34 #include <pulsecore/macro.h>
35 #include <pulsecore/core-util.h>
36 #include <pulse/xmalloc.h>
37
38 #include "fdsem.h"
39
40 struct pa_fdsem {
41 int fds[2];
42 pa_atomic_t waiting;
43 pa_atomic_t signalled;
44 pa_atomic_t in_pipe;
45 };
46
47 pa_fdsem *pa_fdsem_new(void) {
48 pa_fdsem *f;
49
50 f = pa_xnew(pa_fdsem, 1);
51
52 if (pipe(f->fds) < 0) {
53 pa_xfree(f);
54 return NULL;
55 }
56
57 pa_atomic_store(&f->waiting, 0);
58 pa_atomic_store(&f->signalled, 0);
59 pa_atomic_store(&f->in_pipe, 0);
60
61 return f;
62 }
63
64 void pa_fdsem_free(pa_fdsem *f) {
65 pa_assert(f);
66
67 close(f->fds[0]);
68 close(f->fds[1]);
69
70 pa_xfree(f);
71 }
72
73 static void flush(pa_fdsem *f) {
74 ssize_t r;
75 pa_assert(f);
76
77 if (pa_atomic_load(&f->in_pipe) <= 0)
78 return;
79
80 do {
81 char x[10];
82
83 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
84 pa_assert(r < 0 && errno == EINTR);
85 continue;
86 }
87
88 } while (pa_atomic_sub(&f->in_pipe, r) > r);
89 }
90
91 void pa_fdsem_post(pa_fdsem *f) {
92 pa_assert(f);
93
94 if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) {
95
96 if (pa_atomic_load(&f->waiting)) {
97 ssize_t r;
98 char x = 'x';
99
100 pa_atomic_inc(&f->in_pipe);
101
102 for (;;) {
103
104 if ((r = write(f->fds[1], &x, 1)) != 1) {
105 pa_assert(r < 0 && errno == EINTR);
106 continue;
107 }
108
109 break;
110 }
111 }
112 }
113 }
114
115 void pa_fdsem_wait(pa_fdsem *f) {
116 pa_assert(f);
117
118 flush(f);
119
120 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
121 return;
122
123 pa_atomic_inc(&f->waiting);
124
125 while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
126 char x[10];
127 ssize_t r;
128
129 if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
130 pa_assert(r < 0 && errno == EINTR);
131 continue;
132 }
133
134 pa_atomic_sub(&f->in_pipe, r);
135 }
136
137 pa_atomic_dec(&f->waiting);
138 }
139
140 int pa_fdsem_try(pa_fdsem *f) {
141 pa_assert(f);
142
143 flush(f);
144
145 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
146 return 1;
147
148 return 0;
149 }
150
151
152 int pa_fdsem_get(pa_fdsem *f) {
153 pa_assert(f);
154
155 return f->fds[0];
156 }
157
158 int pa_fdsem_before_poll(pa_fdsem *f) {
159 pa_assert(f);
160
161 flush(f);
162
163 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
164 return -1;
165
166 pa_atomic_inc(&f->waiting);
167
168 if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
169 pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
170 return -1;
171 }
172 return 0;
173 }
174
175 int pa_fdsem_after_poll(pa_fdsem *f) {
176 pa_assert(f);
177
178 pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
179
180 flush(f);
181
182 if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
183 return 1;
184
185 return 0;
186 }