From: Abramo Bagnara Date: Tue, 27 Feb 2001 18:21:31 +0000 (+0000) Subject: Added multithread aware status for plugins X-Git-Tag: v1.0.3~938 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=a02e742609bb0ed9610809fc62b5f927dd086e62;p=alsa-lib.git Added multithread aware status for plugins --- diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index c83faaf7..84d5adbb 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -1,13 +1,13 @@ EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = mask.c interval.c \ +libpcm_la_SOURCES = atomic.c mask.c interval.c \ pcm.c pcm_m4.c pcm_hw.c pcm_plugin.c pcm_copy.c pcm_linear.c \ pcm_route.c pcm_mulaw.c pcm_alaw.c pcm_adpcm.c \ pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \ pcm_shm.c pcm_file.c pcm_share.c pcm_null.c \ pcm_params.c -noinst_HEADERS = pcm_local.h pcm_plugin.h mask.h mask_inline.h \ +noinst_HEADERS = atomic.h pcm_local.h pcm_plugin.h mask.h mask_inline.h \ interval.h interval_inline.h plugin_ops.h all: libpcm.la diff --git a/src/pcm/atomic.c b/src/pcm/atomic.c new file mode 100644 index 00000000..6fd5e382 --- /dev/null +++ b/src/pcm/atomic.c @@ -0,0 +1,43 @@ +/* + * Atomic read/write + * Copyright (c) 2001 by Abramo Bagnara + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include "atomic.h" + +void snd_atomic_read_wait(snd_atomic_read_t *t) +{ + volatile const snd_atomic_write_t *w = t->write; + unsigned int loops = 0; + struct timespec ts; + while (w->begin != w->end) { + if (loops < MAX_SPIN_COUNT) { + sched_yield(); + loops++; + continue; + } + loops = 0; + ts.tv_sec = 0; + ts.tv_nsec = SPIN_SLEEP_DURATION; + nanosleep(&ts, NULL); + } +} + diff --git a/src/pcm/atomic.h b/src/pcm/atomic.h new file mode 100644 index 00000000..9698d552 --- /dev/null +++ b/src/pcm/atomic.h @@ -0,0 +1,85 @@ +/* + * Atomic read/write + * Copyright (c) 2001 by Abramo Bagnara + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/* Max number of times we must spin on a spinlock calling sched_yield(). + After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */ + +#ifndef MAX_SPIN_COUNT +#define MAX_SPIN_COUNT 50 +#endif + +/* Duration of sleep (in nanoseconds) when we can't acquire a spinlock + after MAX_SPIN_COUNT iterations of sched_yield(). + This MUST BE > 2ms. + (Otherwise the kernel does busy-waiting for realtime threads, + giving other threads no chance to run.) */ + +#ifndef SPIN_SLEEP_DURATION +#define SPIN_SLEEP_DURATION 2000001 +#endif + +typedef struct { + unsigned int begin, end; +} snd_atomic_write_t; + +typedef struct { + volatile const snd_atomic_write_t *write; + unsigned int end; +} snd_atomic_read_t; + +void snd_atomic_read_wait(snd_atomic_read_t *t); + +static inline void snd_atomic_write_init(snd_atomic_write_t *w) +{ + w->begin = 0; + w->end = 0; +} + +static inline void snd_atomic_write_begin(snd_atomic_write_t *w) +{ + w->begin++; + wmb(); +} + +static inline void snd_atomic_write_end(snd_atomic_write_t *w) +{ + wmb(); + w->end++; +} + +static inline void snd_atomic_read_init(snd_atomic_read_t *r, snd_atomic_write_t *w) +{ + r->write = w; +} + +static inline void snd_atomic_read_begin(snd_atomic_read_t *r) +{ + r->end = r->write->end; + rmb(); +} + +static inline int snd_atomic_read_ok(snd_atomic_read_t *r) +{ + rmb(); + return r->end == r->write->begin; +} + diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 8c4714ec..5263c397 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -214,7 +214,7 @@ static int snd_pcm_hw_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) snd_pcm_hw_t *hw = pcm->private_data; int fd = hw->fd; if (ioctl(fd, SNDRV_PCM_IOCTL_DELAY, delayp) < 0) { - SYSERR("SNDRV_PCM_IOCTL_DELAY failed"); + // SYSERR("SNDRV_PCM_IOCTL_DELAY failed"); return -errno; } return 0; diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 13051555..199db0d1 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -24,6 +24,7 @@ #include #include #include +#include #define _snd_pcm_access_mask _snd_mask #define _snd_pcm_format_mask _snd_mask @@ -486,3 +487,4 @@ int snd_pcm_hw_strategy_simple_choices(snd_pcm_hw_strategy_t *strategy, int orde #define SND_PCM_ACCBIT_MMAP ((1 << (unsigned long) SND_PCM_ACCESS_MMAP_INTERLEAVED) | \ (1 << (unsigned long) SND_PCM_ACCESS_MMAP_NONINTERLEAVED) | \ (1 << (unsigned long) SND_PCM_ACCESS_MMAP_COMPLEX)) + diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index d4e471aa..c0e412df 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -70,18 +70,6 @@ int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) return snd_pcm_channel_info_shm(pcm, info, plugin->shmid); } -int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) -{ - snd_pcm_plugin_t *plugin = pcm->private_data; - int err = snd_pcm_status(plugin->slave, status); - if (err < 0) - return err; - status->avail = snd_pcm_mmap_avail(pcm); - if (plugin->client_frames) - status->avail_max = plugin->client_frames(pcm, status->avail_max); - return 0; -} - snd_pcm_state_t snd_pcm_plugin_state(snd_pcm_t *pcm) { snd_pcm_plugin_t *plugin = pcm->private_data; @@ -104,11 +92,16 @@ int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) int snd_pcm_plugin_prepare(snd_pcm_t *pcm) { snd_pcm_plugin_t *plugin = pcm->private_data; - int err = snd_pcm_prepare(plugin->slave); - if (err < 0) + int err; + snd_atomic_write_begin(&plugin->watom); + err = snd_pcm_prepare(plugin->slave); + if (err < 0) { + snd_atomic_write_end(&plugin->watom); return err; + } plugin->hw_ptr = 0; plugin->appl_ptr = 0; + snd_atomic_write_end(&plugin->watom); if (plugin->init) { err = plugin->init(pcm); if (err < 0) @@ -120,11 +113,16 @@ int snd_pcm_plugin_prepare(snd_pcm_t *pcm) int snd_pcm_plugin_reset(snd_pcm_t *pcm) { snd_pcm_plugin_t *plugin = pcm->private_data; - int err = snd_pcm_reset(plugin->slave); - if (err < 0) + int err; + snd_atomic_write_begin(&plugin->watom); + err = snd_pcm_reset(plugin->slave); + if (err < 0) { + snd_atomic_write_end(&plugin->watom); return err; + } plugin->hw_ptr = 0; plugin->appl_ptr = 0; + snd_atomic_write_end(&plugin->watom); if (plugin->init) { err = plugin->init(pcm); if (err < 0) @@ -172,19 +170,24 @@ snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames /* FIXME: rate plugin */ if (plugin->slave_frames) frames = plugin->slave_frames(pcm, frames); + snd_atomic_write_begin(&plugin->watom); err = snd_pcm_rewind(plugin->slave, frames); if (err < 0) { - if (n <= 0) + if (n <= 0) { + snd_atomic_write_end(&plugin->watom); return err; + } goto _end; } if (plugin->client_frames) err = plugin->client_frames(pcm, err); snd_pcm_mmap_hw_backward(pcm, err); n += err; - } + } else + snd_atomic_write_begin(&plugin->watom); _end: snd_pcm_mmap_appl_backward(pcm, n); + snd_atomic_write_end(&plugin->watom); return n; } @@ -207,9 +210,11 @@ snd_pcm_uframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, frames = plugin->write(pcm, areas, offset, frames, slave_areas, slave_offset, &slave_frames); assert(slave_frames <= snd_pcm_mmap_playback_avail(slave)); + snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); snd_pcm_mmap_hw_forward(pcm, frames); snd_pcm_mmap_forward(slave, slave_frames); + snd_atomic_write_end(&plugin->watom); offset += frames; size -= frames; if (slave_frames == slave_cont) @@ -239,9 +244,11 @@ snd_pcm_uframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, frames = plugin->read(pcm, areas, offset, frames, slave_areas, slave_offset, &slave_frames); assert(slave_frames <= snd_pcm_mmap_capture_avail(slave)); + snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); snd_pcm_mmap_hw_forward(pcm, frames); snd_pcm_mmap_forward(slave, slave_frames); + snd_atomic_write_end(&plugin->watom); offset += frames; size -= frames; if (slave_frames == slave_cont) @@ -293,7 +300,9 @@ snd_pcm_sframes_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t snd_pcm_uframes_t xfer, offset; snd_pcm_uframes_t slave_offset, slave_size; if (pcm->stream == SND_PCM_STREAM_CAPTURE) { + snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, size); + snd_atomic_write_end(&plugin->watom); return size; } slave_size = snd_pcm_avail_update(slave); @@ -315,9 +324,11 @@ snd_pcm_sframes_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t slave_frames = slave_cont; frames = plugin->write(pcm, areas, offset, frames, slave_areas, slave_offset, &slave_frames); + snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); snd_pcm_mmap_hw_forward(pcm, frames); snd_pcm_mmap_forward(slave, slave_frames); + snd_atomic_write_end(&plugin->watom); xfer += frames; if (frames == cont) offset = 0; @@ -365,8 +376,10 @@ snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) slave_frames = slave_cont; frames = plugin->read(pcm, areas, offset, frames, slave_areas, slave_offset, &slave_frames); + snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_hw_forward(pcm, frames); snd_pcm_mmap_forward(slave, slave_frames); + snd_atomic_write_end(&plugin->watom); xfer += frames; if (frames == cont) offset = 0; @@ -411,6 +424,36 @@ int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm) return _snd_pcm_poll_descriptor(plugin->slave); } +int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) +{ + snd_pcm_plugin_t *plugin = pcm->private_data; + snd_pcm_sframes_t err; + snd_atomic_read_t ratom; + snd_atomic_read_init(&ratom, &plugin->watom); + _again: + snd_atomic_read_begin(&ratom); + err = snd_pcm_status(plugin->slave, status); + if (err < 0) { + snd_atomic_read_ok(&ratom); + return err; + } + status->appl_ptr = plugin->appl_ptr; + status->hw_ptr = plugin->hw_ptr; + err = snd_pcm_plugin_avail_update(pcm); + if (err < 0) + status->avail = pcm->buffer_size; + else + status->avail = err; + snd_pcm_plugin_delay(pcm, &status->delay); + if (!snd_atomic_read_ok(&ratom)) { + snd_atomic_read_wait(&ratom); + goto _again; + } + if (plugin->client_frames) + status->avail_max = plugin->client_frames(pcm, status->avail_max); + return 0; +} + int snd_pcm_plugin_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_plugin_t *plugin = pcm->private_data; diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index 760a8a8c..ea8190af 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -19,6 +19,8 @@ * */ +#include "atomic.h" + typedef snd_pcm_uframes_t (*snd_pcm_slave_xfer_areas_func_t) (snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas, @@ -38,6 +40,7 @@ typedef struct { int (*init)(snd_pcm_t *pcm); int shmid; snd_pcm_uframes_t appl_ptr, hw_ptr; + snd_atomic_write_t watom; } snd_pcm_plugin_t; int snd_pcm_plugin_close(snd_pcm_t *pcm);