From 114df1f137d5a32cdde64d43712fb602c2f93ebb Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Sat, 11 Jan 2014 01:01:54 +0100
Subject: [PATCH] DatabasePlugin: add interface DatabaseListener

Allow database plugins to announce that they have been modified.
---
 Makefile.am                     |  2 ++
 src/DatabaseGlue.cxx            |  5 +++--
 src/DatabaseGlue.hxx            |  5 ++++-
 src/DatabaseListener.hxx        | 38 +++++++++++++++++++++++++++++++++
 src/DatabasePlugin.hxx          |  5 ++++-
 src/Instance.cxx                |  6 ++++++
 src/Instance.hxx                |  7 +++++-
 src/Main.cxx                    |  2 +-
 src/db/ProxyDatabasePlugin.cxx  |  7 ++++--
 src/db/SimpleDatabasePlugin.cxx |  4 +++-
 src/db/SimpleDatabasePlugin.hxx |  3 ++-
 src/db/UpnpDatabasePlugin.cxx   |  7 ++++--
 test/DumpDatabase.cxx           | 15 ++++++++++++-
 13 files changed, 93 insertions(+), 13 deletions(-)
 create mode 100644 src/DatabaseListener.hxx

diff --git a/Makefile.am b/Makefile.am
index bcd251cca..66186ec9c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -114,6 +114,7 @@ src_mpd_SOURCES = \
 	src/DatabaseLock.cxx src/DatabaseLock.hxx \
 	src/DatabaseSave.cxx src/DatabaseSave.hxx \
 	src/DatabasePlugin.hxx \
+	src/DatabaseListener.hxx \
 	src/DatabaseVisitor.hxx \
 	src/DatabaseSelection.cxx src/DatabaseSelection.hxx \
 	src/ExcludeList.cxx src/ExcludeList.hxx \
@@ -1202,6 +1203,7 @@ test_DumpDatabase_LDADD = \
 	$(TAG_LIBS) \
 	libconf.a \
 	libutil.a \
+	libevent.a \
 	libsystem.a \
 	libfs.a \
 	$(GLIB_LIBS)
diff --git a/src/DatabaseGlue.cxx b/src/DatabaseGlue.cxx
index f64b31738..d5aad0522 100644
--- a/src/DatabaseGlue.cxx
+++ b/src/DatabaseGlue.cxx
@@ -37,7 +37,8 @@ static bool db_is_open;
 static bool is_simple;
 
 bool
-DatabaseGlobalInit(const config_param &param, Error &error)
+DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener,
+		   const config_param &param, Error &error)
 {
 	assert(db == nullptr);
 	assert(!db_is_open);
@@ -53,7 +54,7 @@ DatabaseGlobalInit(const config_param &param, Error &error)
 		return false;
 	}
 
-	db = plugin->create(param, error);
+	db = plugin->create(loop, listener, param, error);
 	return db != nullptr;
 }
 
diff --git a/src/DatabaseGlue.hxx b/src/DatabaseGlue.hxx
index 725aa302a..bf4699ff6 100644
--- a/src/DatabaseGlue.hxx
+++ b/src/DatabaseGlue.hxx
@@ -23,6 +23,8 @@
 #include "Compiler.h"
 
 struct config_param;
+class EventLoop;
+class DatabaseListener;
 class Database;
 class Error;
 
@@ -32,7 +34,8 @@ class Error;
  * @param param the database configuration block
  */
 bool
-DatabaseGlobalInit(const config_param &param, Error &error);
+DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener,
+		   const config_param &param, Error &error);
 
 void
 DatabaseGlobalDeinit(void);
diff --git a/src/DatabaseListener.hxx b/src/DatabaseListener.hxx
new file mode 100644
index 000000000..4da458866
--- /dev/null
+++ b/src/DatabaseListener.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2014 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_DATABASE_CLIENT_HXX
+#define MPD_DATABASE_CLIENT_HXX
+
+/**
+ * An object that listens to events from the #Database.
+ *
+ * @see #Instance
+ */
+class DatabaseListener {
+public:
+	/**
+	 * The database has been modified.  This must be called in the
+	 * thread that has created the #Database instance and that
+	 * runs the #EventLoop.
+	 */
+	virtual void OnDatabaseModified() = 0;
+};
+
+#endif
diff --git a/src/DatabasePlugin.hxx b/src/DatabasePlugin.hxx
index ccf899389..e42c3bbd3 100644
--- a/src/DatabasePlugin.hxx
+++ b/src/DatabasePlugin.hxx
@@ -37,6 +37,8 @@ struct DatabaseSelection;
 struct db_visitor;
 struct Song;
 class Error;
+class EventLoop;
+class DatabaseListener;
 
 struct DatabaseStats {
 	/**
@@ -149,7 +151,8 @@ struct DatabasePlugin {
 	/**
 	 * Allocates and configures a database.
 	 */
-	Database *(*create)(const config_param &param,
+	Database *(*create)(EventLoop &loop, DatabaseListener &listener,
+			    const config_param &param,
 			    Error &error);
 };
 
diff --git a/src/Instance.cxx b/src/Instance.cxx
index 4fe399a2d..e9a308848 100644
--- a/src/Instance.cxx
+++ b/src/Instance.cxx
@@ -48,3 +48,9 @@ Instance::SyncWithPlayer()
 {
 	partition->SyncWithPlayer();
 }
+
+void
+Instance::OnDatabaseModified()
+{
+	DatabaseModified();
+}
diff --git a/src/Instance.hxx b/src/Instance.hxx
index 45b1e44f1..160e713b0 100644
--- a/src/Instance.hxx
+++ b/src/Instance.hxx
@@ -21,11 +21,13 @@
 #define MPD_INSTANCE_HXX
 
 #include "check.h"
+#include "DatabaseListener.hxx"
+#include "Compiler.h"
 
 class ClientList;
 struct Partition;
 
-struct Instance {
+struct Instance final : public DatabaseListener {
 	ClientList *client_list;
 
 	Partition *partition;
@@ -48,6 +50,9 @@ struct Instance {
 	 * Synchronize the player with the play queue.
 	 */
 	void SyncWithPlayer();
+
+private:
+	virtual void OnDatabaseModified();
 };
 
 #endif
diff --git a/src/Main.cxx b/src/Main.cxx
index da8458340..624c6216d 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -186,7 +186,7 @@ glue_db_init_and_load(void)
 		return true;
 
 	Error error;
-	if (!DatabaseGlobalInit(*param, error))
+	if (!DatabaseGlobalInit(*main_loop, *instance, *param, error))
 		FatalError(error);
 
 	delete allocated;
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
index 344edb900..e8d64003c 100644
--- a/src/db/ProxyDatabasePlugin.cxx
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -49,7 +49,8 @@ class ProxyDatabase : public Database {
 	mutable time_t update_stamp;
 
 public:
-	static Database *Create(const config_param &param,
+	static Database *Create(EventLoop &loop, DatabaseListener &listener,
+				const config_param &param,
 				Error &error);
 
 	virtual bool Open(Error &error) override;
@@ -218,7 +219,9 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
 }
 
 Database *
-ProxyDatabase::Create(const config_param &param, Error &error)
+ProxyDatabase::Create(gcc_unused EventLoop &loop,
+		      gcc_unused DatabaseListener &listener,
+		      const config_param &param, Error &error)
 {
 	ProxyDatabase *db = new ProxyDatabase();
 	if (!db->Configure(param, error)) {
diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx
index a46aa60b2..31d4213a5 100644
--- a/src/db/SimpleDatabasePlugin.cxx
+++ b/src/db/SimpleDatabasePlugin.cxx
@@ -38,7 +38,9 @@
 static constexpr Domain simple_db_domain("simple_db");
 
 Database *
-SimpleDatabase::Create(const config_param &param, Error &error)
+SimpleDatabase::Create(gcc_unused EventLoop &loop,
+		       gcc_unused DatabaseListener &listener,
+		       const config_param &param, Error &error)
 {
 	SimpleDatabase *db = new SimpleDatabase();
 	if (!db->Configure(param, error)) {
diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx
index dfe981dd8..98cbb96f0 100644
--- a/src/db/SimpleDatabasePlugin.hxx
+++ b/src/db/SimpleDatabasePlugin.hxx
@@ -53,7 +53,8 @@ public:
 
 	bool Save(Error &error);
 
-	static Database *Create(const config_param &param,
+	static Database *Create(EventLoop &loop, DatabaseListener &listener,
+				const config_param &param,
 				Error &error);
 
 	virtual bool Open(Error &error) override;
diff --git a/src/db/UpnpDatabasePlugin.cxx b/src/db/UpnpDatabasePlugin.cxx
index ec86aa400..5c15fffeb 100644
--- a/src/db/UpnpDatabasePlugin.cxx
+++ b/src/db/UpnpDatabasePlugin.cxx
@@ -69,7 +69,8 @@ public:
 		: m_lib(0), m_superdir(0), m_root(0)
 	{}
 
-	static Database *Create(const config_param &param,
+	static Database *Create(EventLoop &loop, DatabaseListener &listener,
+				const config_param &param,
 				Error &error);
 
 	virtual bool Open(Error &error) override;
@@ -182,7 +183,9 @@ stringToTokens(const std::string &str,
 }
 
 Database *
-UpnpDatabase::Create(const config_param &param, Error &error)
+UpnpDatabase::Create(gcc_unused EventLoop &loop,
+		     gcc_unused DatabaseListener &listener,
+		     const config_param &param, Error &error)
 {
 	UpnpDatabase *db = new UpnpDatabase();
 	if (db && !db->Configure(param, error)) {
diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx
index 52f7fbd02..ff3464099 100644
--- a/test/DumpDatabase.cxx
+++ b/test/DumpDatabase.cxx
@@ -21,6 +21,7 @@
 #include "DatabaseRegistry.hxx"
 #include "DatabasePlugin.hxx"
 #include "DatabaseSelection.hxx"
+#include "DatabaseListener.hxx"
 #include "Directory.hxx"
 #include "Song.hxx"
 #include "PlaylistVector.hxx"
@@ -28,6 +29,7 @@
 #include "ConfigData.hxx"
 #include "tag/TagConfig.hxx"
 #include "fs/Path.hxx"
+#include "event/Loop.hxx"
 #include "util/Error.hxx"
 
 #include <glib.h>
@@ -48,6 +50,13 @@ InputStream::LockRead(void *, size_t, Error &)
 }
 #endif
 
+class MyDatabaseListener final : public DatabaseListener {
+public:
+	virtual void OnDatabaseModified() override {
+		cout << "DatabaseModified" << endl;
+	}
+};
+
 static bool
 DumpDirectory(const Directory &directory, Error &)
 {
@@ -108,6 +117,9 @@ main(int argc, char **argv)
 
 	TagLoadConfig();
 
+	EventLoop event_loop;
+	MyDatabaseListener database_listener;
+
 	/* do it */
 
 	const struct config_param *path = config_get_param(CONF_DB_FILE);
@@ -115,7 +127,8 @@ main(int argc, char **argv)
 	if (path != nullptr)
 		param.AddBlockParam("path", path->value.c_str(), path->line);
 
-	Database *db = plugin->create(param, error);
+	Database *db = plugin->create(event_loop, database_listener,
+				      param, error);
 
 	if (db == nullptr) {
 		cerr << error.GetMessage() << endl;