]> git.alsa-project.org Git - alsa-lib.git/commitdiff
PCM: Avoid busy loop in snd_pcm_write_areas() with rate plugin
authorTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2012 15:16:26 +0000 (16:16 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2012 15:16:26 +0000 (16:16 +0100)
snd_pcm_write_areas() tries to wait until avail >= avail_min condition
is satisfied.  This doesn't work always well when a rate plugin is in
the play.

When a partial data with a smaller size than a period is written, the
rate plugin doesn't transfer the data immediately to the slave PCM,
but kept in an internal buffer and it changes only the hwptr of the
plugin.  Thus, the condition "avail < avail_min" is triggered for a
wait check although the underlying slave PCM has enough room.  This
results in a call of snd_pcm_wait() which returns immediately after
poll() call, and the snd_pcm_write_areas() loop continues.  As a
consequence, it falls into a CPU hog.

This patch fixes that busy loop by introducing a new fast_ops to check
the availability for wait of avail_min.  Then a plugin can ask the
slave PCM whether the wait is required (or possible).

A few plugins like multi plugin need a special handling.  Otherwise a
generic plugin function can be used.

Reported-by: Trent Piepho <tpiepho@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
src/pcm/pcm.c
src/pcm/pcm_generic.c
src/pcm/pcm_generic.h
src/pcm/pcm_hooks.c
src/pcm/pcm_local.h
src/pcm/pcm_meter.c
src/pcm/pcm_mmap_emul.c
src/pcm/pcm_multi.c
src/pcm/pcm_plugin.c
src/pcm/pcm_rate.c

index 359d2955a1147950a78795cb3ff4d78e9869c74e..f8b68ed99c994092951241d825c4baeb4f7086ec 100644 (file)
@@ -2357,7 +2357,7 @@ int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name,
  */
 int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
 {
-       if (snd_pcm_mmap_avail(pcm) >= pcm->avail_min) {
+       if (!snd_pcm_may_wait_for_avail_min(pcm, snd_pcm_mmap_avail(pcm))) {
                /* check more precisely */
                switch (snd_pcm_state(pcm)) {
                case SND_PCM_STATE_XRUN:
@@ -6776,14 +6776,14 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area
                        goto _end;
                }
                if ((state == SND_PCM_STATE_RUNNING &&
-                    (snd_pcm_uframes_t)avail < pcm->avail_min &&
-                    size > (snd_pcm_uframes_t)avail)) {
+                    size > (snd_pcm_uframes_t)avail &&
+                    snd_pcm_may_wait_for_avail_min(pcm, avail))) {
                        if (pcm->mode & SND_PCM_NONBLOCK) {
                                err = -EAGAIN;
                                goto _end;
                        }
 
-                       err = snd_pcm_wait(pcm, -1);
+                       err = snd_pcm_wait_nocheck(pcm, -1);
                        if (err < 0)
                                break;
                        goto _again;                    
index d56e5d3fce7954222b90b8383c51c9eee71d5162..5fc4888538b8c52b505fde2e8948137bd4367a75 100644 (file)
@@ -341,4 +341,10 @@ int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
        return snd_pcm_set_chmap(generic->slave, map);
 }
 
+int snd_pcm_generic_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail ATTRIBUTE_UNUSED)
+{
+       snd_pcm_generic_t *generic = pcm->private_data;
+       return snd_pcm_may_wait_for_avail_min(generic->slave, snd_pcm_mmap_avail(generic->slave));
+}
+
 #endif /* DOC_HIDDEN */
index 916c6ecaa048409d47e60cae0b361b27b9d28965..d18907744ad72be85822b9b585ab87c7931c6c70 100644 (file)
@@ -109,6 +109,8 @@ typedef struct {
        snd1_pcm_generic_get_chmap
 #define snd_pcm_generic_set_chmap \
        snd1_pcm_generic_set_chmap
+#define snd_pcm_generic_may_wait_for_avail_min \
+       snd1_pcm_generic_may_wait_for_avail_min
 
 int snd_pcm_generic_close(snd_pcm_t *pcm);
 int snd_pcm_generic_nonblock(snd_pcm_t *pcm, int nonblock);
@@ -158,5 +160,5 @@ int snd_pcm_generic_munmap(snd_pcm_t *pcm);
 snd_pcm_chmap_query_t **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm);
 snd_pcm_chmap_t *snd_pcm_generic_get_chmap(snd_pcm_t *pcm);
 int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map);
-
+int snd_pcm_generic_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail);
 
index 0feb4a3a96b0328ab83c3ae87b24e90cb33c867c..f83728245ecd224842688994bf5806be5852cd48 100644 (file)
@@ -199,6 +199,7 @@ static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = {
        .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
        .poll_descriptors = snd_pcm_generic_poll_descriptors,
        .poll_revents = snd_pcm_generic_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
 };
 
 /**
index e7798fd0ae115458ab9ed9a640cc4eea8db7ee84..8cf7c3d70831b42d172ebf4c79909657fba67358 100644 (file)
@@ -177,6 +177,7 @@ typedef struct {
        int (*poll_descriptors_count)(snd_pcm_t *pcm);
        int (*poll_descriptors)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
        int (*poll_revents)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
+       int (*may_wait_for_avail_min)(snd_pcm_t *pcm, snd_pcm_uframes_t avail);
 } snd_pcm_fast_ops_t;
 
 struct _snd_pcm {
@@ -984,3 +985,13 @@ _snd_pcm_parse_config_chmaps(snd_config_t *conf);
 snd_pcm_chmap_t *
 _snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps);
 
+/* return true if the PCM stream may wait to get avail_min space */
+static inline int snd_pcm_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
+{
+       if (avail >= pcm->avail_min)
+               return 0;
+       if (pcm->fast_ops->may_wait_for_avail_min)
+               return pcm->fast_ops->may_wait_for_avail_min(pcm, avail);
+       return 1;
+}
+
index 42a125e5cae0cc692203952efd1502ee8aedffd7..e60b92d999add1ae48865f557f528f599f3bf302 100644 (file)
@@ -545,6 +545,7 @@ static const snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = {
        .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
        .poll_descriptors = snd_pcm_generic_poll_descriptors,
        .poll_revents = snd_pcm_generic_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
 };
 
 /**
index 811cb1cb7a3b939924e24b9c0bd39f4b0be398cb..67d2e050c1a8aa0ced9ef48c268c489c0dcf8459 100644 (file)
@@ -400,6 +400,7 @@ static const snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = {
        .poll_descriptors = snd_pcm_generic_poll_descriptors,
        .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
        .poll_revents = snd_pcm_generic_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
 };
 
 #ifndef DOC_HIDDEN
index ffb1b5329702093188c75bc8a7d8e2a65c36aa46..2db82c0cca548226744b833dd96e3069ac981ca4 100644 (file)
@@ -739,6 +739,13 @@ static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
        return 0;
 }
 
+static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
+{
+       snd_pcm_multi_t *multi = pcm->private_data;
+       snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
+       return snd_pcm_may_wait_for_avail_min(slave, snd_pcm_mmap_avail(slave));
+}
+
 static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm)
 {
        snd_pcm_multi_t *multi = pcm->private_data;
@@ -937,6 +944,7 @@ static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
        .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
        .poll_descriptors = snd_pcm_multi_poll_descriptors,
        .poll_revents = snd_pcm_multi_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min,
 };
 
 /**
index d88e11782ac4147edd1643456d577f805b131cfd..96218a8d0e06ac050c4a0d04a07f8e5b60e7c16d 100644 (file)
@@ -570,6 +570,7 @@ const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
        .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
        .poll_descriptors = snd_pcm_generic_poll_descriptors,
        .poll_revents = snd_pcm_generic_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
 };
 
 #endif
index 4ba8521ea394ff08ab1b4c2b34555257ce8969db..54a3e670fd506095652a2cfedbd76489111bb42f 100644 (file)
@@ -1234,6 +1234,7 @@ static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
        .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
        .poll_descriptors = snd_pcm_generic_poll_descriptors,
        .poll_revents = snd_pcm_rate_poll_revents,
+       .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
 };
 
 static const snd_pcm_ops_t snd_pcm_rate_ops = {