~diwic/ubuntu/lucid/pulseaudio/bugfixes

« back to all changes in this revision

Viewing changes to src/pulsecore/lock-autospawn.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich
  • Date: 2008-11-04 15:46:00 UTC
  • mfrom: (1.2.1 upstream) (1.1.6 lenny)
  • Revision ID: james.westby@ubuntu.com-20081104154600-hlzknpcazaam0nxm
Tags: 0.9.13-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Don't build against, and create jack package. Jack is not in main.
  - Remove --disable-per-user-esound-socket from configure flags, as we still
    want per user esound sockets.
  - Remove stop links from rc0 and rc6.
  - Change default resample algorithm and bubffer size.
  - Add alsa configuration files to route alsa applications via pulseaudio.
  - Move libasound2-plugins from Recommends to Depends.
* debian/pulseaudio.preinst: When upgrading from intrepid, remove
  /etc/X11/Xsession.d/70pulseaudio, as this was used to minimize a race
  condition when starting GNOME in intrepid. This race should not exist in
  jaunty once libcanberra is built to use pulseaudio as a backend.
* Do not spawn a pulseaudio server if clients fail to find a running server.
* Remove explicit version dependency for libspeex-dev to allow the package
  to be built for now.
* Regenerate autotools files to work with Ubuntu's newer libtool/libltdl.
* debian/control: libpulsecore5 -> libpulsecore8 to match the library
  soname.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***
 
2
  This file is part of PulseAudio.
 
3
 
 
4
  Copyright 2008 Lennart Poettering
 
5
 
 
6
  PulseAudio is free software; you can redistribute it and/or modify
 
7
  it under the terms of the GNU Lesser General Public License as published
 
8
  by the Free Software Foundation; either version 2 of the License,
 
9
  or (at your option) any later version.
 
10
 
 
11
  PulseAudio is distributed in the hope that it will be useful, but
 
12
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
14
  General Public License for more details.
 
15
 
 
16
  You should have received a copy of the GNU Lesser General Public License
 
17
  along with PulseAudio; if not, write to the Free Software
 
18
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 
19
  USA.
 
20
***/
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <fcntl.h>
 
27
#include <errno.h>
 
28
#include <string.h>
 
29
#include <sys/poll.h>
 
30
#include <signal.h>
 
31
#include <pthread.h>
 
32
 
 
33
#include <pulse/i18n.h>
 
34
#include <pulse/xmalloc.h>
 
35
 
 
36
#include <pulsecore/mutex.h>
 
37
#include <pulsecore/thread.h>
 
38
#include <pulsecore/core-util.h>
 
39
 
 
40
#include "lock-autospawn.h"
 
41
 
 
42
/* So, why do we have this complex code here with threads and pipes
 
43
 * and stuff? For two reasons: POSIX file locks are per-process, not
 
44
 * per-file descriptor. That means that two contexts within the same
 
45
 * process that try to create the autospawn lock might end up assuming
 
46
 * they both managed to lock the file. And then, POSIX locking
 
47
 * operations are synchronous. If two contexts run from the same event
 
48
 * loop it must be made sure that they do not block each other, but
 
49
 * that the locking operation can happen asynchronously. */
 
50
 
 
51
#define AUTOSPAWN_LOCK "autospawn.lock"
 
52
 
 
53
static pa_mutex *mutex;
 
54
 
 
55
static unsigned n_ref = 0;
 
56
static int lock_fd = -1;
 
57
static pa_mutex *lock_fd_mutex = NULL;
 
58
static pa_bool_t taken = FALSE;
 
59
static pa_thread *thread;
 
60
static int pipe_fd[2] = { -1, -1 };
 
61
 
 
62
static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
 
63
 
 
64
static int ref(void) {
 
65
 
 
66
    if (n_ref > 0) {
 
67
 
 
68
        pa_assert(pipe_fd[0] >= 0);
 
69
        pa_assert(pipe_fd[1] >= 0);
 
70
 
 
71
        n_ref++;
 
72
 
 
73
        return 0;
 
74
    }
 
75
 
 
76
    pa_assert(lock_fd < 0);
 
77
    pa_assert(!lock_fd_mutex);
 
78
    pa_assert(!taken);
 
79
    pa_assert(!thread);
 
80
    pa_assert(pipe_fd[0] < 0);
 
81
    pa_assert(pipe_fd[1] < 0);
 
82
 
 
83
    if (pipe(pipe_fd) < 0)
 
84
        return -1;
 
85
 
 
86
    lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
 
87
 
 
88
    pa_make_fd_cloexec(pipe_fd[0]);
 
89
    pa_make_fd_cloexec(pipe_fd[1]);
 
90
 
 
91
    pa_make_fd_nonblock(pipe_fd[1]);
 
92
    pa_make_fd_nonblock(pipe_fd[0]);
 
93
 
 
94
    n_ref = 1;
 
95
    return 0;
 
96
}
 
97
 
 
98
static void unref(pa_bool_t after_fork) {
 
99
 
 
100
    pa_assert(n_ref > 0);
 
101
    pa_assert(pipe_fd[0] >= 0);
 
102
    pa_assert(pipe_fd[1] >= 0);
 
103
    pa_assert(lock_fd_mutex);
 
104
 
 
105
    n_ref--;
 
106
 
 
107
    if (n_ref > 0)
 
108
        return;
 
109
 
 
110
    pa_assert(!taken);
 
111
 
 
112
    if (thread) {
 
113
        pa_thread_free(thread);
 
114
        thread = NULL;
 
115
    }
 
116
 
 
117
    pa_mutex_lock(lock_fd_mutex);
 
118
    if (lock_fd >= 0) {
 
119
 
 
120
        if (after_fork)
 
121
            pa_close(lock_fd);
 
122
        else {
 
123
            char *lf;
 
124
 
 
125
            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
 
126
                pa_log_warn(_("Cannot access autospawn lock."));
 
127
 
 
128
            pa_unlock_lockfile(lf, lock_fd);
 
129
            pa_xfree(lf);
 
130
 
 
131
            lock_fd = -1;
 
132
        }
 
133
    }
 
134
    pa_mutex_unlock(lock_fd_mutex);
 
135
 
 
136
    pa_mutex_free(lock_fd_mutex);
 
137
    lock_fd_mutex = NULL;
 
138
 
 
139
    pa_close(pipe_fd[0]);
 
140
    pa_close(pipe_fd[1]);
 
141
    pipe_fd[0] = pipe_fd[1] = -1;
 
142
}
 
143
 
 
144
static void ping(void) {
 
145
    ssize_t s;
 
146
 
 
147
    pa_assert(pipe_fd[1] >= 0);
 
148
 
 
149
    for (;;) {
 
150
        char x = 'x';
 
151
 
 
152
        if ((s = write(pipe_fd[1], &x, 1)) == 1)
 
153
            break;
 
154
 
 
155
        pa_assert(s < 0);
 
156
 
 
157
        if (errno == EAGAIN)
 
158
            break;
 
159
 
 
160
        pa_assert(errno == EINTR);
 
161
    }
 
162
}
 
163
 
 
164
static void wait_for_ping(void) {
 
165
    ssize_t s;
 
166
    char x;
 
167
    struct pollfd pfd;
 
168
    int k;
 
169
 
 
170
    pa_assert(pipe_fd[0] >= 0);
 
171
 
 
172
    memset(&pfd, 0, sizeof(pfd));
 
173
    pfd.fd = pipe_fd[0];
 
174
    pfd.events = POLLIN;
 
175
 
 
176
    if ((k = poll(&pfd, 1, -1)) != 1) {
 
177
        pa_assert(k < 0);
 
178
        pa_assert(errno == EINTR);
 
179
    } else if ((s = read(pipe_fd[0], &x, 1)) != 1) {
 
180
        pa_assert(s < 0);
 
181
        pa_assert(errno == EAGAIN);
 
182
    }
 
183
}
 
184
 
 
185
static void empty_pipe(void) {
 
186
    char x[16];
 
187
    ssize_t s;
 
188
 
 
189
    pa_assert(pipe_fd[0] >= 0);
 
190
 
 
191
    if ((s = read(pipe_fd[0], &x, sizeof(x))) < 1) {
 
192
        pa_assert(s < 0);
 
193
        pa_assert(errno == EAGAIN);
 
194
    }
 
195
}
 
196
 
 
197
static void thread_func(void *u) {
 
198
    int fd;
 
199
    char *lf;
 
200
    sigset_t fullset;
 
201
 
 
202
    /* No signals in this thread please */
 
203
    sigfillset(&fullset);
 
204
    pthread_sigmask(SIG_BLOCK, &fullset, NULL);
 
205
 
 
206
    if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
 
207
        pa_log_warn(_("Cannot access autospawn lock."));
 
208
        goto finish;
 
209
    }
 
210
 
 
211
    if ((fd = pa_lock_lockfile(lf)) < 0)
 
212
        goto finish;
 
213
 
 
214
    pa_mutex_lock(lock_fd_mutex);
 
215
    pa_assert(lock_fd < 0);
 
216
    lock_fd = fd;
 
217
    pa_mutex_unlock(lock_fd_mutex);
 
218
 
 
219
finish:
 
220
    pa_xfree(lf);
 
221
 
 
222
    ping();
 
223
}
 
224
 
 
225
static int start_thread(void) {
 
226
 
 
227
    if (!thread)
 
228
        if (!(thread = pa_thread_new(thread_func, NULL)))
 
229
            return -1;
 
230
 
 
231
    return 0;
 
232
}
 
233
 
 
234
static void create_mutex(void) {
 
235
    PA_ONCE_BEGIN {
 
236
        mutex = pa_mutex_new(FALSE, FALSE);
 
237
    } PA_ONCE_END;
 
238
}
 
239
 
 
240
static void destroy_mutex(void) {
 
241
 
 
242
    if (mutex)
 
243
        pa_mutex_free(mutex);
 
244
}
 
245
 
 
246
 
 
247
int pa_autospawn_lock_init(void) {
 
248
    int ret = -1;
 
249
 
 
250
    create_mutex();
 
251
    pa_mutex_lock(mutex);
 
252
 
 
253
    if (ref() < 0)
 
254
        ret = -1;
 
255
    else
 
256
        ret = pipe_fd[0];
 
257
 
 
258
    pa_mutex_unlock(mutex);
 
259
 
 
260
    return ret;
 
261
}
 
262
 
 
263
int pa_autospawn_lock_acquire(pa_bool_t block) {
 
264
    int ret = -1;
 
265
 
 
266
    create_mutex();
 
267
    pa_mutex_lock(mutex);
 
268
    pa_assert(n_ref >= 1);
 
269
 
 
270
    pa_mutex_lock(lock_fd_mutex);
 
271
 
 
272
    for (;;) {
 
273
 
 
274
        empty_pipe();
 
275
 
 
276
        if (lock_fd >= 0 && !taken) {
 
277
            taken = TRUE;
 
278
            ret = 1;
 
279
            break;
 
280
        }
 
281
 
 
282
        if (lock_fd < 0)
 
283
            if (start_thread() < 0)
 
284
                break;
 
285
 
 
286
        if (!block) {
 
287
            ret = 0;
 
288
            break;
 
289
        }
 
290
 
 
291
        pa_mutex_unlock(lock_fd_mutex);
 
292
        pa_mutex_unlock(mutex);
 
293
 
 
294
        wait_for_ping();
 
295
 
 
296
        pa_mutex_lock(mutex);
 
297
        pa_mutex_lock(lock_fd_mutex);
 
298
    }
 
299
 
 
300
    pa_mutex_unlock(lock_fd_mutex);
 
301
 
 
302
    pa_mutex_unlock(mutex);
 
303
 
 
304
    return ret;
 
305
}
 
306
 
 
307
void pa_autospawn_lock_release(void) {
 
308
 
 
309
    create_mutex();
 
310
    pa_mutex_lock(mutex);
 
311
    pa_assert(n_ref >= 1);
 
312
 
 
313
    pa_assert(taken);
 
314
    taken = FALSE;
 
315
 
 
316
    ping();
 
317
 
 
318
    pa_mutex_unlock(mutex);
 
319
}
 
320
 
 
321
void pa_autospawn_lock_done(pa_bool_t after_fork) {
 
322
 
 
323
    create_mutex();
 
324
    pa_mutex_lock(mutex);
 
325
    pa_assert(n_ref >= 1);
 
326
 
 
327
    unref(after_fork);
 
328
 
 
329
    pa_mutex_unlock(mutex);
 
330
}