From 8c425c758c9217b3388ca5bc1ea2883a1c3c2bc6 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 8 Aug 2012 21:54:54 +0200
Subject: [PATCH] decoder_control: add GError attribute

---
 Makefile.am           |  1 +
 src/decoder_control.c |  3 +++
 src/decoder_control.h | 51 +++++++++++++++++++++++++++++++++++++++++++
 src/decoder_error.h   | 35 +++++++++++++++++++++++++++++
 src/decoder_thread.c  | 18 ++++++++++++++-
 5 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 src/decoder_error.h

diff --git a/Makefile.am b/Makefile.am
index dc3b04658..e43f4e799 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -247,6 +247,7 @@ src_mpd_SOURCES = \
 	src/conf.c \
 	src/crossfade.c \
 	src/cue/cue_parser.c src/cue/cue_parser.h \
+	src/decoder_error.h \
 	src/decoder_thread.c \
 	src/decoder_control.c \
 	src/decoder_api.c \
diff --git a/src/decoder_control.c b/src/decoder_control.c
index 70f34b331..caa3705f5 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -52,6 +52,8 @@ dc_new(GCond *client_cond)
 void
 dc_free(struct decoder_control *dc)
 {
+	dc_clear_error(dc);
+
 	g_cond_free(dc->cond);
 	g_mutex_free(dc->mutex);
 	g_free(dc->mixramp_start);
@@ -79,6 +81,7 @@ static void
 dc_command(struct decoder_control *dc, enum decoder_command cmd)
 {
 	decoder_lock(dc);
+	dc_clear_error(dc);
 	dc_command_locked(dc, cmd);
 	decoder_unlock(dc);
 }
diff --git a/src/decoder_control.h b/src/decoder_control.h
index 566b153ee..164875ae0 100644
--- a/src/decoder_control.h
+++ b/src/decoder_control.h
@@ -67,6 +67,14 @@ struct decoder_control {
 	enum decoder_state state;
 	enum decoder_command command;
 
+	/**
+	 * The error that occurred in the decoder thread.  This
+	 * attribute is only valid if #state is #DECODE_STATE_ERROR.
+	 * The object must be freed when this object transitions to
+	 * any other state (usually #DECODE_STATE_START).
+	 */
+	GError *error;
+
 	bool quit;
 	bool seek_error;
 	bool seekable;
@@ -188,6 +196,49 @@ decoder_has_failed(const struct decoder_control *dc)
 	return dc->state == DECODE_STATE_ERROR;
 }
 
+/**
+ * Checks whether an error has occurred, and if so, returns a newly
+ * allocated copy of the #GError object.
+ *
+ * Caller must lock the object.
+ */
+static inline GError *
+dc_get_error(const struct decoder_control *dc)
+{
+	assert(dc != NULL);
+	assert(dc->command == DECODE_COMMAND_NONE);
+
+	return dc->state == DECODE_STATE_ERROR
+		? g_error_copy(dc->error)
+		: NULL;
+}
+
+/**
+ * Like dc_get_error(), but locks and unlocks the object.
+ */
+static inline GError *
+dc_lock_get_error(struct decoder_control *dc)
+{
+	decoder_lock(dc);
+	GError *error = dc_get_error(dc);
+	decoder_unlock(dc);
+	return error;
+}
+
+/**
+ * Clear the error condition and free the #GError object (if any).
+ *
+ * Caller must lock the object.
+ */
+static inline void
+dc_clear_error(struct decoder_control *dc)
+{
+	if (dc->state == DECODE_STATE_ERROR) {
+		g_error_free(dc->error);
+		dc->state = DECODE_STATE_STOP;
+	}
+}
+
 static inline bool
 decoder_lock_is_idle(struct decoder_control *dc)
 {
diff --git a/src/decoder_error.h b/src/decoder_error.h
new file mode 100644
index 000000000..a12a31937
--- /dev/null
+++ b/src/decoder_error.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_ERROR_H
+#define MPD_DECODER_ERROR_H
+
+#include <glib.h>
+
+/**
+ * Quark for GError.domain.
+ */
+G_GNUC_CONST
+static inline GQuark
+decoder_quark(void)
+{
+	return g_quark_from_static_string("decoder");
+}
+
+#endif
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index 1440fc272..11d96b35c 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -19,6 +19,7 @@
 
 #include "config.h"
 #include "decoder_thread.h"
+#include "decoder_error.h"
 #include "decoder_control.h"
 #include "decoder_internal.h"
 #include "decoder_list.h"
@@ -428,12 +429,27 @@ decoder_run_song(struct decoder_control *dc,
 
 	decoder_lock(dc);
 
-	dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
+	if (ret)
+		dc->state = DECODE_STATE_STOP;
+	else {
+		dc->state = DECODE_STATE_ERROR;
+
+		const char *error_uri = song->uri;
+		char *allocated = uri_remove_auth(error_uri);
+		if (allocated != NULL)
+			error_uri = allocated;
+
+		dc->error = g_error_new(decoder_quark(), 0,
+					"Failed to decode %s", error_uri);
+		g_free(allocated);
+	}
 }
 
 static void
 decoder_run(struct decoder_control *dc)
 {
+	dc_clear_error(dc);
+
 	const struct song *song = dc->song;
 	char *uri;