~binli/ubuntu/vivid/pulseaudio/load-extcon-module

« back to all changes in this revision

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

  • Committer: Bin Li
  • Date: 2016-01-23 15:04:48 UTC
  • Revision ID: bin.li@canonical.com-20160123150448-5ockvw4p5xxntda4
init the 1:6.0-0ubuntu9.15 from silo 12

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.1 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, see <http://www.gnu.org/licenses/>.
 
18
***/
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include <config.h>
 
22
#endif
 
23
 
 
24
#include <errno.h>
 
25
#include <string.h>
 
26
#include <signal.h>
 
27
 
 
28
#ifdef HAVE_PTHREAD
 
29
#include <pthread.h>
 
30
#endif
 
31
 
 
32
#include <pulse/gccmacro.h>
 
33
#include <pulse/xmalloc.h>
 
34
 
 
35
#include <pulsecore/i18n.h>
 
36
#include <pulsecore/poll.h>
 
37
#include <pulsecore/mutex.h>
 
38
#include <pulsecore/thread.h>
 
39
#include <pulsecore/core-util.h>
 
40
 
 
41
#include "lock-autospawn.h"
 
42
 
 
43
/* So, why do we have this complex code here with threads and pipes
 
44
 * and stuff? For two reasons: POSIX file locks are per-process, not
 
45
 * per-file descriptor. That means that two contexts within the same
 
46
 * process that try to create the autospawn lock might end up assuming
 
47
 * they both managed to lock the file. And then, POSIX locking
 
48
 * operations are synchronous. If two contexts run from the same event
 
49
 * loop it must be made sure that they do not block each other, but
 
50
 * that the locking operation can happen asynchronously. */
 
51
 
 
52
#define AUTOSPAWN_LOCK "autospawn.lock"
 
53
 
 
54
static pa_mutex *mutex;
 
55
 
 
56
static unsigned n_ref = 0;
 
57
static int lock_fd = -1;
 
58
static pa_mutex *lock_fd_mutex = NULL;
 
59
static pa_thread *thread = NULL;
 
60
static int pipe_fd[2] = { -1, -1 };
 
61
 
 
62
static enum {
 
63
    STATE_IDLE,
 
64
    STATE_OWNING,
 
65
    STATE_TAKEN,
 
66
    STATE_FAILED
 
67
} state = STATE_IDLE;
 
68
 
 
69
static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
 
70
 
 
71
static int ref(void) {
 
72
 
 
73
    if (n_ref > 0) {
 
74
 
 
75
        pa_assert(pipe_fd[0] >= 0);
 
76
        pa_assert(pipe_fd[1] >= 0);
 
77
        pa_assert(lock_fd_mutex);
 
78
 
 
79
        n_ref++;
 
80
 
 
81
        return 0;
 
82
    }
 
83
 
 
84
    pa_assert(!lock_fd_mutex);
 
85
    pa_assert(state == STATE_IDLE);
 
86
    pa_assert(lock_fd < 0);
 
87
    pa_assert(!thread);
 
88
    pa_assert(pipe_fd[0] < 0);
 
89
    pa_assert(pipe_fd[1] < 0);
 
90
 
 
91
    if (pa_pipe_cloexec(pipe_fd) < 0)
 
92
        return -1;
 
93
 
 
94
    pa_make_fd_nonblock(pipe_fd[1]);
 
95
    pa_make_fd_nonblock(pipe_fd[0]);
 
96
 
 
97
    lock_fd_mutex = pa_mutex_new(false, false);
 
98
 
 
99
    n_ref = 1;
 
100
    return 0;
 
101
}
 
102
 
 
103
static void unref(bool after_fork) {
 
104
 
 
105
    pa_assert(n_ref > 0);
 
106
    pa_assert(pipe_fd[0] >= 0);
 
107
    pa_assert(pipe_fd[1] >= 0);
 
108
    pa_assert(lock_fd_mutex);
 
109
 
 
110
    n_ref--;
 
111
 
 
112
    if (n_ref > 0)
 
113
        return;
 
114
 
 
115
    /* Join threads only in the process the new thread was created in
 
116
     * to avoid undefined behaviour.
 
117
     * POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume
 
118
     * that thread IDs are usable and unique within a single process." */
 
119
    if (thread) {
 
120
        if (after_fork)
 
121
            pa_thread_free_nojoin(thread);
 
122
        else
 
123
            pa_thread_free(thread);
 
124
        thread = NULL;
 
125
    }
 
126
 
 
127
    pa_mutex_lock(lock_fd_mutex);
 
128
 
 
129
    pa_assert(state != STATE_TAKEN);
 
130
 
 
131
    if (state == STATE_OWNING) {
 
132
 
 
133
        pa_assert(lock_fd >= 0);
 
134
 
 
135
        if (after_fork)
 
136
            pa_close(lock_fd);
 
137
        else {
 
138
            char *lf;
 
139
 
 
140
            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
 
141
                pa_log_warn(_("Cannot access autospawn lock."));
 
142
 
 
143
            pa_unlock_lockfile(lf, lock_fd);
 
144
            pa_xfree(lf);
 
145
        }
 
146
    }
 
147
 
 
148
    lock_fd = -1;
 
149
    state = STATE_IDLE;
 
150
 
 
151
    pa_mutex_unlock(lock_fd_mutex);
 
152
 
 
153
    pa_mutex_free(lock_fd_mutex);
 
154
    lock_fd_mutex = NULL;
 
155
 
 
156
    pa_close(pipe_fd[0]);
 
157
    pa_close(pipe_fd[1]);
 
158
    pipe_fd[0] = pipe_fd[1] = -1;
 
159
}
 
160
 
 
161
static void ping(void) {
 
162
    ssize_t s;
 
163
 
 
164
    pa_assert(pipe_fd[1] >= 0);
 
165
 
 
166
    for (;;) {
 
167
        char x = 'x';
 
168
 
 
169
        if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
 
170
            break;
 
171
 
 
172
        pa_assert(s < 0);
 
173
 
 
174
        if (errno == EAGAIN)
 
175
            break;
 
176
 
 
177
        pa_assert(errno == EINTR);
 
178
    }
 
179
}
 
180
 
 
181
static void wait_for_ping(void) {
 
182
    ssize_t s;
 
183
    char x;
 
184
    struct pollfd pfd;
 
185
    int k;
 
186
 
 
187
    pa_assert(pipe_fd[0] >= 0);
 
188
 
 
189
    memset(&pfd, 0, sizeof(pfd));
 
190
    pfd.fd = pipe_fd[0];
 
191
    pfd.events = POLLIN;
 
192
 
 
193
    if ((k = pa_poll(&pfd, 1, -1)) != 1) {
 
194
        pa_assert(k < 0);
 
195
        pa_assert(errno == EINTR);
 
196
    } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
 
197
        pa_assert(s < 0);
 
198
        pa_assert(errno == EAGAIN);
 
199
    }
 
200
}
 
201
 
 
202
static void empty_pipe(void) {
 
203
    char x[16];
 
204
    ssize_t s;
 
205
 
 
206
    pa_assert(pipe_fd[0] >= 0);
 
207
 
 
208
    if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
 
209
        pa_assert(s < 0);
 
210
        pa_assert(errno == EAGAIN);
 
211
    }
 
212
}
 
213
 
 
214
static void thread_func(void *u) {
 
215
    int fd;
 
216
    char *lf;
 
217
 
 
218
#ifdef HAVE_PTHREAD
 
219
    sigset_t fullset;
 
220
 
 
221
    /* No signals in this thread please */
 
222
    sigfillset(&fullset);
 
223
    pthread_sigmask(SIG_BLOCK, &fullset, NULL);
 
224
#endif
 
225
 
 
226
    if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
 
227
        pa_log_warn(_("Cannot access autospawn lock."));
 
228
        goto fail;
 
229
    }
 
230
 
 
231
    if ((fd = pa_lock_lockfile(lf)) < 0)
 
232
        goto fail;
 
233
 
 
234
    pa_mutex_lock(lock_fd_mutex);
 
235
    pa_assert(state == STATE_IDLE);
 
236
    lock_fd = fd;
 
237
    state = STATE_OWNING;
 
238
    pa_mutex_unlock(lock_fd_mutex);
 
239
 
 
240
    goto finish;
 
241
 
 
242
fail:
 
243
    pa_mutex_lock(lock_fd_mutex);
 
244
    pa_assert(state == STATE_IDLE);
 
245
    state = STATE_FAILED;
 
246
    pa_mutex_unlock(lock_fd_mutex);
 
247
 
 
248
finish:
 
249
    pa_xfree(lf);
 
250
 
 
251
    ping();
 
252
}
 
253
 
 
254
static int start_thread(void) {
 
255
 
 
256
    if (!thread)
 
257
        if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
 
258
            return -1;
 
259
 
 
260
    return 0;
 
261
}
 
262
 
 
263
static void create_mutex(void) {
 
264
    PA_ONCE_BEGIN {
 
265
        mutex = pa_mutex_new(false, false);
 
266
    } PA_ONCE_END;
 
267
}
 
268
 
 
269
static void destroy_mutex(void) {
 
270
    if (mutex)
 
271
        pa_mutex_free(mutex);
 
272
}
 
273
 
 
274
int pa_autospawn_lock_init(void) {
 
275
    int ret = -1;
 
276
 
 
277
    create_mutex();
 
278
    pa_mutex_lock(mutex);
 
279
 
 
280
    if (ref() < 0)
 
281
        ret = -1;
 
282
    else
 
283
        ret = pipe_fd[0];
 
284
 
 
285
    pa_mutex_unlock(mutex);
 
286
 
 
287
    return ret;
 
288
}
 
289
 
 
290
int pa_autospawn_lock_acquire(bool block) {
 
291
    int ret = -1;
 
292
 
 
293
    create_mutex();
 
294
    pa_mutex_lock(mutex);
 
295
    pa_assert(n_ref >= 1);
 
296
 
 
297
    pa_mutex_lock(lock_fd_mutex);
 
298
 
 
299
    for (;;) {
 
300
 
 
301
        empty_pipe();
 
302
 
 
303
        if (state == STATE_OWNING) {
 
304
            state = STATE_TAKEN;
 
305
            ret = 1;
 
306
            break;
 
307
        }
 
308
 
 
309
        if (state == STATE_FAILED) {
 
310
            ret = -1;
 
311
            break;
 
312
        }
 
313
 
 
314
        if (state == STATE_IDLE)
 
315
            if (start_thread() < 0)
 
316
                break;
 
317
 
 
318
        if (!block) {
 
319
            ret = 0;
 
320
            break;
 
321
        }
 
322
 
 
323
        pa_mutex_unlock(lock_fd_mutex);
 
324
        pa_mutex_unlock(mutex);
 
325
 
 
326
        wait_for_ping();
 
327
 
 
328
        pa_mutex_lock(mutex);
 
329
        pa_mutex_lock(lock_fd_mutex);
 
330
    }
 
331
 
 
332
    pa_mutex_unlock(lock_fd_mutex);
 
333
 
 
334
    pa_mutex_unlock(mutex);
 
335
 
 
336
    return ret;
 
337
}
 
338
 
 
339
void pa_autospawn_lock_release(void) {
 
340
 
 
341
    create_mutex();
 
342
    pa_mutex_lock(mutex);
 
343
    pa_assert(n_ref >= 1);
 
344
 
 
345
    pa_assert(state == STATE_TAKEN);
 
346
    state = STATE_OWNING;
 
347
 
 
348
    ping();
 
349
 
 
350
    pa_mutex_unlock(mutex);
 
351
}
 
352
 
 
353
void pa_autospawn_lock_done(bool after_fork) {
 
354
 
 
355
    create_mutex();
 
356
    pa_mutex_lock(mutex);
 
357
    pa_assert(n_ref >= 1);
 
358
 
 
359
    unref(after_fork);
 
360
 
 
361
    pa_mutex_unlock(mutex);
 
362
}