This fixes threading bugs guenther found when we switched to
rthreads. Most changes are copy & paste from audacious.

Basically, call any sio_* function with the mutex locked. But since
we can't hold the mutex locked during blocking operations (the GUI
would block) use a simple "event loop" with non-blocking i/o.

Regressions? Comments? OKs?

-- Alexandre

Index: Makefile
===================================================================
RCS file: /cvs/ports/audio/xmms/Makefile,v
retrieving revision 1.79
diff -u -p -r1.79 Makefile
--- Makefile    9 Aug 2012 17:54:47 -0000       1.79
+++ Makefile    26 Aug 2012 19:21:35 -0000
@@ -10,7 +10,7 @@ SHARED_ONLY=          Yes
 VERSION=               1.2.11
 DISTNAME=              xmms-${VERSION}
 PKGNAME-main=          xmms-${VERSION}
-REVISION-main=         10
+REVISION-main=         11
 PKGNAME-vorbis=                xmms-vorbis-${VERSION}
 REVISION-vorbis=       4
 PKGNAME-mikmod=                xmms-mikmod-${VERSION}
Index: files/audio_sndio.c
===================================================================
RCS file: /cvs/ports/audio/xmms/files/audio_sndio.c,v
retrieving revision 1.3
diff -u -p -r1.3 audio_sndio.c
--- files/audio_sndio.c 5 Dec 2010 15:52:19 -0000       1.3
+++ files/audio_sndio.c 26 Aug 2012 19:21:35 -0000
@@ -14,9 +14,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <errno.h>
+#include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <gtk/gtk.h>
 #include <libxmms/util.h>
@@ -54,8 +57,8 @@ static struct sio_par par;
 static struct sio_hdl *hdl;
 static long long rdpos;
 static long long wrpos;
-static int paused;
-static int volume = XMMS_MAXVOL;
+static int paused, restarted, volume;
+static int pause_pending, flush_pending, volume_pending;
 static long bytes_per_sec;
 static AFormat afmt;
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -91,6 +94,69 @@ get_oplugin_info (void)
 }
 
 static void
+reset(void)
+{
+       if (!restarted) {
+               restarted = 1;
+               sio_stop(hdl);
+               sio_start(hdl);
+               rdpos = wrpos;
+       }
+}
+static void
+onmove_cb (void *addr, int delta)
+{
+       rdpos += delta * (int)(par.bps * par.pchan);
+}
+
+static void
+onvol_cb(void *addr, unsigned ctl)
+{
+       /* Update volume only if it actually changed */
+       if (ctl != volume * SIO_MAXVOL / 100)
+               volume = ctl * 100 / SIO_MAXVOL;
+}
+
+static void
+wait_ready(void)
+{
+       int n;
+       struct pollfd pfds[16];
+
+       if (volume_pending) {
+               sio_setvol(hdl, volume * SIO_MAXVOL / 100);
+               volume_pending = 0;
+       }
+       if (flush_pending) {
+               reset();
+               flush_pending = 0;
+       }
+       if (pause_pending) {
+               if (paused)
+                       reset();
+               pause_pending = 0;
+       }
+       if (paused) {
+               pthread_mutex_unlock(&mutex);
+               usleep(20000);
+               pthread_mutex_lock(&mutex);
+               return;
+       }
+       n = sio_pollfd(hdl, pfds, POLLOUT);
+       if (n != 0) {
+               pthread_mutex_unlock(&mutex);
+               while (poll(pfds, n, -1) < 0) {
+                       if (errno != EINTR) {
+                               perror("poll");
+                               exit(1);
+                       }
+               }
+               pthread_mutex_lock(&mutex);
+       }
+       (void)sio_revents(hdl, pfds);
+}
+
+static void
 op_about (void)
 {
        static GtkWidget *about;
@@ -118,7 +184,7 @@ op_init (void)
        xmms_cfg_free(cfgfile);
 
        if (!audiodev)
-               audiodev = g_strdup("");
+               audiodev = g_strdup(SIO_DEVANY);
 }
 
 static void
@@ -146,13 +212,11 @@ op_open (AFormat fmt, int rate, int nch)
        struct sio_par askpar;
 
        pthread_mutex_lock (&mutex);
-
-       hdl = sio_open (strlen (audiodev) > 0 ? audiodev : NULL, SIO_PLAY, 0);
+       hdl = sio_open (audiodev, SIO_PLAY, 1);
        if (hdl == NULL) {
                fprintf (stderr, "%s: failed to open audio device\n", __func__);
                goto error;
        }
-
        sio_initpar (&par);
        afmt = fmt;
        switch (fmt) {
@@ -225,17 +289,18 @@ op_open (AFormat fmt, int rate, int nch)
        rdpos = 0;
        wrpos = 0;
        sio_onmove (hdl, onmove_cb, NULL);
-
-       paused = 0;
+       sio_onvol (hdl, onvol_cb, NULL);
+       sio_setvol(hdl, volume * SIO_MAXVOL / 100);
        if (!sio_start (hdl)) {
                fprintf (stderr, "%s: failed to start audio device\n",
                        __func__);
                goto error;
        }
-
        bytes_per_sec = par.bps * par.pchan * par.rate;
+       pause_pending = flush_pending = volume_pending = 0;
+       restarted = 1;
+       paused = 0;
        pthread_mutex_unlock (&mutex);
-       op_set_volume (volume, volume);
        return TRUE;
 
 error:
@@ -247,11 +312,9 @@ error:
 static void
 op_write (void *ptr, int len)
 {
+       unsigned n;
        EffectPlugin *ep;
 
-       if (paused)
-               return;
-
        /* This sucks but XMMS totally broke the effect plugin code when
           they added support for multiple enabled effects.  Complain to
           the non-existent XMMS team if a plugin does not work, however
@@ -259,13 +322,22 @@ op_write (void *ptr, int len)
        ep = get_current_effect_plugin ();
        ep->mod_samples (&ptr, len, afmt, par.rate, par.pchan);
 
-       /* Do not lock sio_write as this will cause the GUI thread
-          to block waiting for a blocked sio_write to return. */
-       len = sio_write (hdl, ptr, len);
-
-       pthread_mutex_lock (&mutex);
-       wrpos += len;
-       pthread_mutex_unlock (&mutex);
+       pthread_mutex_lock(&mutex);
+       for (;;) {
+               if (paused)
+                       break;
+               restarted = 0;
+               n = sio_write(hdl, ptr, len);
+               if (n == 0 && sio_eof(hdl))
+                       return;
+               wrpos += n;
+               len -= n;
+               ptr = (char *)ptr + n;
+               if (len == 0)
+                       break;
+               wait_ready();
+       }
+       pthread_mutex_unlock(&mutex);
 }
 
 static void
@@ -280,28 +352,30 @@ op_close (void)
 }
 
 static void
-op_seek (int time_ms)
+op_seek (int time)
 {
-       int bufused;
+       int used;
 
        pthread_mutex_lock (&mutex);
-       bufused = (rdpos < 0) ? wrpos : wrpos - rdpos;
-       rdpos = time_ms / 1000 * bytes_per_sec;
-       wrpos = rdpos + bufused;
+       used = wrpos - rdpos;
+       rdpos = (long long)time * bytes_per_sec / 1000;
+       wrpos = rdpos + used;
        pthread_mutex_unlock (&mutex);
 }
 
 static void
 op_pause (short flag)
 {
+       pthread_mutex_lock(&mutex);
        paused = flag;
+       pause_pending = 1;
+       pthread_mutex_unlock(&mutex);
 }
 
 static int
 op_buffer_free (void)
 {
-#define MAGIC 1000000 /* See Output/{OSS,sun,esd}/audio.c */
-       return paused ? 0 : MAGIC; 
+       return paused ? 0 : par.round * par.pchan * par.bps;
 }
 
 static int
@@ -333,14 +407,6 @@ op_get_written_time (void)
 }
 
 static void
-onmove_cb (void *addr, int delta)
-{
-       pthread_mutex_lock (&mutex);
-       rdpos += delta * (int)(par.bps * par.pchan);
-       pthread_mutex_unlock (&mutex);
-}
-
-static void
 op_configure(void)
 {
        GtkWidget *dev_vbox;
@@ -370,9 +436,6 @@ op_configure(void)
        adevice_vbox = gtk_vbox_new(FALSE, 5);
        gtk_container_set_border_width(GTK_CONTAINER(adevice_vbox), 5);
        gtk_container_add(GTK_CONTAINER(adevice_frame), adevice_vbox);
-
-       adevice_text = gtk_label_new(_("(empty means default)"));
-       gtk_box_pack_start_defaults(GTK_BOX(adevice_vbox), adevice_text);
 
        adevice_entry = gtk_entry_new();
        gtk_entry_set_text(GTK_ENTRY(adevice_entry), audiodev);

Reply via email to