From 8351543c0f43ee281032ef712cc5168beb662d31 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 10 Jan 2014 23:21:53 +0100 Subject: db/upnp: move lazy Open() call to new class LazyDatabase Generic approach for the workaround. --- src/db/LazyDatabase.cxx | 103 ++++++++++++++++++++++++++++++++++++++++++ src/db/LazyDatabase.hxx | 69 ++++++++++++++++++++++++++++ src/db/UpnpDatabasePlugin.cxx | 38 ++++------------ 3 files changed, 181 insertions(+), 29 deletions(-) create mode 100644 src/db/LazyDatabase.cxx create mode 100644 src/db/LazyDatabase.hxx (limited to 'src') diff --git a/src/db/LazyDatabase.cxx b/src/db/LazyDatabase.cxx new file mode 100644 index 000000000..f767e89cd --- /dev/null +++ b/src/db/LazyDatabase.cxx @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#include "config.h" +#include "LazyDatabase.hxx" + +#include + +LazyDatabase::~LazyDatabase() +{ + assert(!open); + + delete db; +} + +bool +LazyDatabase::EnsureOpen(Error &error) const +{ + if (open) + return true; + + if (!db->Open(error)) + return false; + + open = true; + return true; +} + +void +LazyDatabase::Close() +{ + if (open) { + open = false; + db->Close(); + } +} + +Song * +LazyDatabase::GetSong(const char *uri, Error &error) const +{ + return EnsureOpen(error) + ? db->GetSong(uri, error) + : nullptr; +} + +void +LazyDatabase::ReturnSong(Song *song) const +{ + assert(open); + + db->ReturnSong(song); +} + +bool +LazyDatabase::Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + Error &error) const +{ + return EnsureOpen(error) && + db->Visit(selection, visit_directory, visit_song, + visit_playlist, error); +} + +bool +LazyDatabase::VisitUniqueTags(const DatabaseSelection &selection, + TagType tag_type, + VisitString visit_string, + Error &error) const +{ + return EnsureOpen(error) && + db->VisitUniqueTags(selection, tag_type, visit_string, error); +} + +bool +LazyDatabase::GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, Error &error) const +{ + return EnsureOpen(error) && db->GetStats(selection, stats, error); +} + +time_t +LazyDatabase::GetUpdateStamp() const +{ + return open ? db->GetUpdateStamp() : 0; +} diff --git a/src/db/LazyDatabase.hxx b/src/db/LazyDatabase.hxx new file mode 100644 index 000000000..3910cb7fa --- /dev/null +++ b/src/db/LazyDatabase.hxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003-2013 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_LAZY_DATABASE_PLUGIN_HXX +#define MPD_LAZY_DATABASE_PLUGIN_HXX + +#include "DatabasePlugin.hxx" + +/** + * A wrapper for a #Database object that gets opened on the first + * database access. This works around daemonization problems with + * some plugins. + */ +class LazyDatabase final : public Database { + Database *const db; + + mutable bool open; + +public: + gcc_nonnull_all + LazyDatabase(Database *_db) + :db(_db), open(false) {} + + virtual ~LazyDatabase(); + + virtual void Close() override; + + virtual Song *GetSong(const char *uri_utf8, + Error &error) const override; + virtual void ReturnSong(Song *song) const; + + virtual bool Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + Error &error) const override; + + virtual bool VisitUniqueTags(const DatabaseSelection &selection, + TagType tag_type, + VisitString visit_string, + Error &error) const override; + + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + Error &error) const override; + + virtual time_t GetUpdateStamp() const override; + +private: + bool EnsureOpen(Error &error) const; +}; + +#endif diff --git a/src/db/UpnpDatabasePlugin.cxx b/src/db/UpnpDatabasePlugin.cxx index 5c15fffeb..dbc0cdfd7 100644 --- a/src/db/UpnpDatabasePlugin.cxx +++ b/src/db/UpnpDatabasePlugin.cxx @@ -24,6 +24,7 @@ #include "upnp/Discovery.hxx" #include "upnp/ContentDirectoryService.hxx" #include "upnp/Directory.hxx" +#include "LazyDatabase.hxx" #include "DatabasePlugin.hxx" #include "DatabaseSelection.hxx" #include "DatabaseError.hxx" @@ -99,7 +100,6 @@ protected: bool Configure(const config_param ¶m, Error &error); private: - bool reallyOpen(Error &error); bool VisitServer(ContentDirectoryService* server, const std::vector &vpath, const DatabaseSelection &selection, @@ -188,11 +188,15 @@ UpnpDatabase::Create(gcc_unused EventLoop &loop, const config_param ¶m, Error &error) { UpnpDatabase *db = new UpnpDatabase(); - if (db && !db->Configure(param, error)) { + if (!db->Configure(param, error)) { delete db; - db = nullptr; + return nullptr; } - return db; + + /* libupnp loses its ability to receive multicast messages + apparently due to daemonization; using the LazyDatabase + wrapper works around this problem */ + return new LazyDatabase(db); } bool @@ -201,23 +205,8 @@ UpnpDatabase::Configure(const config_param &, Error &) return true; } -// Open is called before mpd daemonizes, and daemonizing messes with -// the UpNP lib (it loses its ability to receive mcast messages -// apparently). -// So we delay actual opening until the first client call. -// -// Unfortunately the servers may take a few seconds to answer the -// first search on the network, so we would either have to sleep for a -// relatively long time (maybe 5S), or accept that the directory may -// not be complete on the first client call. There is no simple -// solution to this issue, we would need a call from mpd after daemonizing. bool -UpnpDatabase::Open(Error &) -{ - return true; -} - -bool UpnpDatabase::reallyOpen(Error &error) +UpnpDatabase::Open(Error &error) { if (m_root) return true; @@ -320,9 +309,6 @@ upnpItemToSong(const UPnPDirObject &dirent, const char *uri) Song * UpnpDatabase::GetSong(const char *uri, Error &error) const { - if (!const_cast(this)->reallyOpen(error)) - return nullptr; - if (!m_superdir || !m_superdir->ok()) { error.Set(upnp_domain, "UpnpDatabase::GetSong() superdir is sick"); @@ -813,9 +799,6 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, VisitPlaylist visit_playlist, Error &error) const { - if (!const_cast(this)->reallyOpen(error)) - return false; - std::vector servers; if (!m_superdir || !m_superdir->ok() || !m_superdir->getDirServices(servers)) { @@ -873,9 +856,6 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, VisitString visit_string, Error &error) const { - if (!const_cast(this)->reallyOpen(error)) - return false; - if (!visit_string) return true; -- cgit v1.2.3