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#include <unistd.h> 2553a5a1b3Sopenharmony_ci#include <errno.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include "fdsem.h" 3553a5a1b3Sopenharmony_ci 3653a5a1b3Sopenharmony_ci/* For debugging purposes we can define _Y to put and extra thread 3753a5a1b3Sopenharmony_ci * yield between each operation. */ 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ci/* #define PROFILE */ 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_ci#ifdef PROFILE 4253a5a1b3Sopenharmony_ci#define _Y pa_thread_yield() 4353a5a1b3Sopenharmony_ci#else 4453a5a1b3Sopenharmony_ci#define _Y do { } while(0) 4553a5a1b3Sopenharmony_ci#endif 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_cistruct pa_shmasyncq { 4853a5a1b3Sopenharmony_ci pa_fdsem *read_fdsem, *write_fdsem; 4953a5a1b3Sopenharmony_ci pa_shmasyncq_data *data; 5053a5a1b3Sopenharmony_ci}; 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_cistatic int is_power_of_two(unsigned size) { 5353a5a1b3Sopenharmony_ci return !(size & (size - 1)); 5453a5a1b3Sopenharmony_ci} 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_cistatic int reduce(pa_shmasyncq *l, int value) { 5753a5a1b3Sopenharmony_ci return value & (unsigned) (l->n_elements - 1); 5853a5a1b3Sopenharmony_ci} 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_cistatic pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) { 6153a5a1b3Sopenharmony_ci pa_assert(i < l->data->n_elements); 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_ci return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size))) 6453a5a1b3Sopenharmony_ci} 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_cistatic void *get_cell_data(pa_atomic_t *a) { 6753a5a1b3Sopenharmony_ci return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t)); 6853a5a1b3Sopenharmony_ci} 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_cipa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) { 7153a5a1b3Sopenharmony_ci pa_shmasyncq *l; 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_ci pa_assert(n_elements > 0); 7453a5a1b3Sopenharmony_ci pa_assert(is_power_of_two(n_elements)); 7553a5a1b3Sopenharmony_ci pa_assert(element_size > 0); 7653a5a1b3Sopenharmony_ci pa_assert(data); 7753a5a1b3Sopenharmony_ci pa_assert(fd); 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci l = pa_xnew(pa_shmasyncq, 1); 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci l->data = data; 8253a5a1b3Sopenharmony_ci memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size)); 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci l->data->n_elements = n_elements; 8553a5a1b3Sopenharmony_ci l->data->element_size = element_size; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data))) { 8853a5a1b3Sopenharmony_ci pa_xfree(l); 8953a5a1b3Sopenharmony_ci return NULL; 9053a5a1b3Sopenharmony_ci } 9153a5a1b3Sopenharmony_ci fd[0] = pa_fdsem_get(l->read_fdsem); 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_ci if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) { 9453a5a1b3Sopenharmony_ci pa_fdsem_free(l->read_fdsem); 9553a5a1b3Sopenharmony_ci pa_xfree(l); 9653a5a1b3Sopenharmony_ci return NULL; 9753a5a1b3Sopenharmony_ci } 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci return l; 10053a5a1b3Sopenharmony_ci} 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_civoid pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) { 10353a5a1b3Sopenharmony_ci pa_assert(l); 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ci if (free_cb) { 10653a5a1b3Sopenharmony_ci void *p; 10753a5a1b3Sopenharmony_ci 10853a5a1b3Sopenharmony_ci while ((p = pa_shmasyncq_pop(l, 0))) 10953a5a1b3Sopenharmony_ci free_cb(p); 11053a5a1b3Sopenharmony_ci } 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci pa_fdsem_free(l->read_fdsem); 11353a5a1b3Sopenharmony_ci pa_fdsem_free(l->write_fdsem); 11453a5a1b3Sopenharmony_ci pa_xfree(l); 11553a5a1b3Sopenharmony_ci} 11653a5a1b3Sopenharmony_ci 11753a5a1b3Sopenharmony_ciint pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) { 11853a5a1b3Sopenharmony_ci int idx; 11953a5a1b3Sopenharmony_ci pa_atomic_ptr_t *cells; 12053a5a1b3Sopenharmony_ci 12153a5a1b3Sopenharmony_ci pa_assert(l); 12253a5a1b3Sopenharmony_ci pa_assert(p); 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci cells = PA_SHMASYNCQ_CELLS(l); 12553a5a1b3Sopenharmony_ci 12653a5a1b3Sopenharmony_ci _Y; 12753a5a1b3Sopenharmony_ci idx = reduce(l, l->write_idx); 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) { 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci if (!wait) 13253a5a1b3Sopenharmony_ci return -1; 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci/* pa_log("sleeping on push"); */ 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci do { 13753a5a1b3Sopenharmony_ci pa_fdsem_wait(l->read_fdsem); 13853a5a1b3Sopenharmony_ci } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)); 13953a5a1b3Sopenharmony_ci } 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci _Y; 14253a5a1b3Sopenharmony_ci l->write_idx++; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci pa_fdsem_post(l->write_fdsem); 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci return 0; 14753a5a1b3Sopenharmony_ci} 14853a5a1b3Sopenharmony_ci 14953a5a1b3Sopenharmony_civoid* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) { 15053a5a1b3Sopenharmony_ci int idx; 15153a5a1b3Sopenharmony_ci void *ret; 15253a5a1b3Sopenharmony_ci pa_atomic_ptr_t *cells; 15353a5a1b3Sopenharmony_ci 15453a5a1b3Sopenharmony_ci pa_assert(l); 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci cells = PA_SHMASYNCQ_CELLS(l); 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci _Y; 15953a5a1b3Sopenharmony_ci idx = reduce(l, l->read_idx); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci if (!(ret = pa_atomic_ptr_load(&cells[idx]))) { 16253a5a1b3Sopenharmony_ci 16353a5a1b3Sopenharmony_ci if (!wait) 16453a5a1b3Sopenharmony_ci return NULL; 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci/* pa_log("sleeping on pop"); */ 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci do { 16953a5a1b3Sopenharmony_ci pa_fdsem_wait(l->write_fdsem); 17053a5a1b3Sopenharmony_ci } while (!(ret = pa_atomic_ptr_load(&cells[idx]))); 17153a5a1b3Sopenharmony_ci } 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci pa_assert(ret); 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci /* Guaranteed to succeed if we only have a single reader */ 17653a5a1b3Sopenharmony_ci pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL)); 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_ci _Y; 17953a5a1b3Sopenharmony_ci l->read_idx++; 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_ci pa_fdsem_post(l->read_fdsem); 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci return ret; 18453a5a1b3Sopenharmony_ci} 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ciint pa_shmasyncq_get_fd(pa_shmasyncq *q) { 18753a5a1b3Sopenharmony_ci pa_assert(q); 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci return pa_fdsem_get(q->write_fdsem); 19053a5a1b3Sopenharmony_ci} 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ciint pa_shmasyncq_before_poll(pa_shmasyncq *l) { 19353a5a1b3Sopenharmony_ci int idx; 19453a5a1b3Sopenharmony_ci pa_atomic_ptr_t *cells; 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci pa_assert(l); 19753a5a1b3Sopenharmony_ci 19853a5a1b3Sopenharmony_ci cells = PA_SHMASYNCQ_CELLS(l); 19953a5a1b3Sopenharmony_ci 20053a5a1b3Sopenharmony_ci _Y; 20153a5a1b3Sopenharmony_ci idx = reduce(l, l->read_idx); 20253a5a1b3Sopenharmony_ci 20353a5a1b3Sopenharmony_ci for (;;) { 20453a5a1b3Sopenharmony_ci if (pa_atomic_ptr_load(&cells[idx])) 20553a5a1b3Sopenharmony_ci return -1; 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_ci if (pa_fdsem_before_poll(l->write_fdsem) >= 0) 20853a5a1b3Sopenharmony_ci return 0; 20953a5a1b3Sopenharmony_ci } 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci return 0; 21253a5a1b3Sopenharmony_ci} 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_civoid pa_shmasyncq_after_poll(pa_shmasyncq *l) { 21553a5a1b3Sopenharmony_ci pa_assert(l); 21653a5a1b3Sopenharmony_ci 21753a5a1b3Sopenharmony_ci pa_fdsem_after_poll(l->write_fdsem); 21853a5a1b3Sopenharmony_ci} 219