153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
853a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
953a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  Lesser General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1753a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_SYSCALL_H
2553a5a1b3Sopenharmony_ci#include <sys/syscall.h>
2653a5a1b3Sopenharmony_ci#endif
2753a5a1b3Sopenharmony_ci
2853a5a1b3Sopenharmony_ci#include <unistd.h>
2953a5a1b3Sopenharmony_ci#include <errno.h>
3053a5a1b3Sopenharmony_ci
3153a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h>
3253a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3353a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
3653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#ifndef HAVE_PIPE
3953a5a1b3Sopenharmony_ci#include <pulsecore/pipe.h>
4053a5a1b3Sopenharmony_ci#endif
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
4353a5a1b3Sopenharmony_ci#include <sys/eventfd.h>
4453a5a1b3Sopenharmony_ci#endif
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci#include "fdsem.h"
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_cistruct pa_fdsem {
4953a5a1b3Sopenharmony_ci    int fds[2];
5053a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
5153a5a1b3Sopenharmony_ci    int efd;
5253a5a1b3Sopenharmony_ci#endif
5353a5a1b3Sopenharmony_ci    int write_type;
5453a5a1b3Sopenharmony_ci    pa_fdsem_data *data;
5553a5a1b3Sopenharmony_ci};
5653a5a1b3Sopenharmony_ci
5753a5a1b3Sopenharmony_cipa_fdsem *pa_fdsem_new(void) {
5853a5a1b3Sopenharmony_ci    pa_fdsem *f;
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_ci    f = pa_xmalloc0(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
6353a5a1b3Sopenharmony_ci    if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0)
6453a5a1b3Sopenharmony_ci        f->fds[0] = f->fds[1] = -1;
6553a5a1b3Sopenharmony_ci    else
6653a5a1b3Sopenharmony_ci#endif
6753a5a1b3Sopenharmony_ci    {
6853a5a1b3Sopenharmony_ci        if (pa_pipe_cloexec(f->fds) < 0) {
6953a5a1b3Sopenharmony_ci            pa_xfree(f);
7053a5a1b3Sopenharmony_ci            return NULL;
7153a5a1b3Sopenharmony_ci        }
7253a5a1b3Sopenharmony_ci    }
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->waiting, 0);
7753a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->signalled, 0);
7853a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->in_pipe, 0);
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    return f;
8153a5a1b3Sopenharmony_ci}
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_cipa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
8453a5a1b3Sopenharmony_ci    pa_fdsem *f = NULL;
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    pa_assert(data);
8753a5a1b3Sopenharmony_ci    pa_assert(event_fd >= 0);
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
9053a5a1b3Sopenharmony_ci    f = pa_xnew0(pa_fdsem, 1);
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    f->efd = event_fd;
9353a5a1b3Sopenharmony_ci    pa_make_fd_cloexec(f->efd);
9453a5a1b3Sopenharmony_ci    f->fds[0] = f->fds[1] = -1;
9553a5a1b3Sopenharmony_ci    f->data = data;
9653a5a1b3Sopenharmony_ci#endif
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci    return f;
9953a5a1b3Sopenharmony_ci}
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_cipa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data) {
10253a5a1b3Sopenharmony_ci    pa_fdsem *f = NULL;
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_ci    pa_assert(data);
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci    f = pa_xnew0(pa_fdsem, 1);
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci    if ((f->efd = eventfd(0, EFD_CLOEXEC)) < 0) {
11153a5a1b3Sopenharmony_ci        pa_xfree(f);
11253a5a1b3Sopenharmony_ci        return NULL;
11353a5a1b3Sopenharmony_ci    }
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    f->fds[0] = f->fds[1] = -1;
11653a5a1b3Sopenharmony_ci    f->data = data;
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->waiting, 0);
11953a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->signalled, 0);
12053a5a1b3Sopenharmony_ci    pa_atomic_store(&f->data->in_pipe, 0);
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci#endif
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    return f;
12553a5a1b3Sopenharmony_ci}
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_civoid pa_fdsem_free(pa_fdsem *f) {
12853a5a1b3Sopenharmony_ci    pa_assert(f);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
13153a5a1b3Sopenharmony_ci    if (f->efd >= 0)
13253a5a1b3Sopenharmony_ci        pa_close(f->efd);
13353a5a1b3Sopenharmony_ci#endif
13453a5a1b3Sopenharmony_ci    pa_close_pipe(f->fds);
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci    pa_xfree(f);
13753a5a1b3Sopenharmony_ci}
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_cistatic void flush(pa_fdsem *f) {
14053a5a1b3Sopenharmony_ci    ssize_t r;
14153a5a1b3Sopenharmony_ci    pa_assert(f);
14253a5a1b3Sopenharmony_ci
14353a5a1b3Sopenharmony_ci    if (pa_atomic_load(&f->data->in_pipe) <= 0)
14453a5a1b3Sopenharmony_ci        return;
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci    do {
14753a5a1b3Sopenharmony_ci        char x[10];
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
15053a5a1b3Sopenharmony_ci        if (f->efd >= 0) {
15153a5a1b3Sopenharmony_ci            uint64_t u;
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci            if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
15453a5a1b3Sopenharmony_ci                pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
15553a5a1b3Sopenharmony_ci                pa_assert_not_reached();
15653a5a1b3Sopenharmony_ci            }
15753a5a1b3Sopenharmony_ci            r = (ssize_t) u;
15853a5a1b3Sopenharmony_ci        } else
15953a5a1b3Sopenharmony_ci#endif
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci        if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
16253a5a1b3Sopenharmony_ci            pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
16353a5a1b3Sopenharmony_ci            pa_assert_not_reached();
16453a5a1b3Sopenharmony_ci        }
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    } while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) r);
16753a5a1b3Sopenharmony_ci}
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_civoid pa_fdsem_post(pa_fdsem *f) {
17053a5a1b3Sopenharmony_ci    pa_assert(f);
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci        if (pa_atomic_load(&f->data->waiting)) {
17553a5a1b3Sopenharmony_ci            ssize_t r;
17653a5a1b3Sopenharmony_ci            char x = 'x';
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci            pa_atomic_inc(&f->data->in_pipe);
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci            for (;;) {
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
18353a5a1b3Sopenharmony_ci                if (f->efd >= 0) {
18453a5a1b3Sopenharmony_ci                    uint64_t u = 1;
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_ci                    if ((r = pa_write(f->efd, &u, sizeof(u), &f->write_type)) != sizeof(u)) {
18753a5a1b3Sopenharmony_ci                        pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
18853a5a1b3Sopenharmony_ci                        pa_assert_not_reached();
18953a5a1b3Sopenharmony_ci                    }
19053a5a1b3Sopenharmony_ci                } else
19153a5a1b3Sopenharmony_ci#endif
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci                if ((r = pa_write(f->fds[1], &x, 1, &f->write_type)) != 1) {
19453a5a1b3Sopenharmony_ci                    pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
19553a5a1b3Sopenharmony_ci                    pa_assert_not_reached();
19653a5a1b3Sopenharmony_ci                }
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci                break;
19953a5a1b3Sopenharmony_ci            }
20053a5a1b3Sopenharmony_ci        }
20153a5a1b3Sopenharmony_ci    }
20253a5a1b3Sopenharmony_ci}
20353a5a1b3Sopenharmony_ci
20453a5a1b3Sopenharmony_civoid pa_fdsem_wait(pa_fdsem *f) {
20553a5a1b3Sopenharmony_ci    pa_assert(f);
20653a5a1b3Sopenharmony_ci
20753a5a1b3Sopenharmony_ci    flush(f);
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
21053a5a1b3Sopenharmony_ci        return;
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci    pa_atomic_inc(&f->data->waiting);
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
21553a5a1b3Sopenharmony_ci        char x[10];
21653a5a1b3Sopenharmony_ci        ssize_t r;
21753a5a1b3Sopenharmony_ci
21853a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
21953a5a1b3Sopenharmony_ci        if (f->efd >= 0) {
22053a5a1b3Sopenharmony_ci            uint64_t u;
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci            if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
22353a5a1b3Sopenharmony_ci                pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
22453a5a1b3Sopenharmony_ci                pa_assert_not_reached();
22553a5a1b3Sopenharmony_ci            }
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci            r = (ssize_t) u;
22853a5a1b3Sopenharmony_ci        } else
22953a5a1b3Sopenharmony_ci#endif
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci        if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
23253a5a1b3Sopenharmony_ci            pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
23353a5a1b3Sopenharmony_ci            pa_assert_not_reached();
23453a5a1b3Sopenharmony_ci        }
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci        pa_atomic_sub(&f->data->in_pipe, (int) r);
23753a5a1b3Sopenharmony_ci    }
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
24053a5a1b3Sopenharmony_ci}
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ciint pa_fdsem_try(pa_fdsem *f) {
24353a5a1b3Sopenharmony_ci    pa_assert(f);
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci    flush(f);
24653a5a1b3Sopenharmony_ci
24753a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
24853a5a1b3Sopenharmony_ci        return 1;
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci    return 0;
25153a5a1b3Sopenharmony_ci}
25253a5a1b3Sopenharmony_ci
25353a5a1b3Sopenharmony_ciint pa_fdsem_get(pa_fdsem *f) {
25453a5a1b3Sopenharmony_ci    pa_assert(f);
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_EVENTFD_H
25753a5a1b3Sopenharmony_ci    if (f->efd >= 0)
25853a5a1b3Sopenharmony_ci        return f->efd;
25953a5a1b3Sopenharmony_ci#endif
26053a5a1b3Sopenharmony_ci
26153a5a1b3Sopenharmony_ci    return f->fds[0];
26253a5a1b3Sopenharmony_ci}
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_ciint pa_fdsem_before_poll(pa_fdsem *f) {
26553a5a1b3Sopenharmony_ci    pa_assert(f);
26653a5a1b3Sopenharmony_ci
26753a5a1b3Sopenharmony_ci    flush(f);
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
27053a5a1b3Sopenharmony_ci        return -1;
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci    pa_atomic_inc(&f->data->waiting);
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
27553a5a1b3Sopenharmony_ci        pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
27653a5a1b3Sopenharmony_ci        return -1;
27753a5a1b3Sopenharmony_ci    }
27853a5a1b3Sopenharmony_ci    return 0;
27953a5a1b3Sopenharmony_ci}
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ciint pa_fdsem_after_poll(pa_fdsem *f) {
28253a5a1b3Sopenharmony_ci    pa_assert(f);
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci    flush(f);
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
28953a5a1b3Sopenharmony_ci        return 1;
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    return 0;
29253a5a1b3Sopenharmony_ci}
293