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);