]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Added multithread aware status for plugins
authorAbramo Bagnara <abramo@alsa-project.org>
Tue, 27 Feb 2001 18:21:31 +0000 (18:21 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Tue, 27 Feb 2001 18:21:31 +0000 (18:21 +0000)
src/pcm/Makefile.am
src/pcm/atomic.c [new file with mode: 0644]
src/pcm/atomic.h [new file with mode: 0644]
src/pcm/pcm_hw.c
src/pcm/pcm_local.h
src/pcm/pcm_plugin.c
src/pcm/pcm_plugin.h

index c83faaf7550bf4ec920d525bd9c156f193e9c8bc..84d5adbb5e7f5a17056189158799eeb3845c7d6e 100644 (file)
@@ -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 (file)
index 0000000..6fd5e38
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  Atomic read/write
+ *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *   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 <stdlib.h>
+#include <time.h>
+#include <sched.h>
+#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 (file)
index 0000000..9698d55
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  Atomic read/write
+ *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *   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 <asm/system.h>
+
+/* 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;
+}
+
index 8c4714eca5fd96494a123e4b62c2b4c1470e5e4f..5263c397357265031d47cce622fa5dbc3ea0d166 100644 (file)
@@ -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;
index 13051555b5ada0576923e4e18c01e11830b9218f..199db0d19dc8c94f79a5ce87518fa5a02ece9dd1 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <limits.h>
 #include <sys/uio.h>
+#include <asm/system.h>
 
 #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))
+
index d4e471aaa79af24d887a4ff3605045131bcee654..c0e412df666ea42622ea2f52c91989b28b3c597b 100644 (file)
@@ -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;
index 760a8a8c77eba10e3cd0630efd0ddbcb8def2845..ea8190af2ac3c707d2690a20cb13a2b738f08b22 100644 (file)
@@ -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);