From c6725884bc155da431889468c86c546f0f64f9a1 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 29 Jan 2014 20:16:43 +0100 Subject: [PATCH] db/update: convert to OO API Move global variables into the new classes. That may allow multiple update threads for multiple databases one day. --- Makefile.am | 18 +-- src/GlobalEvents.hxx | 3 - src/Instance.hxx | 3 + src/Main.cxx | 17 ++- src/command/OtherCommands.cxx | 21 ++- src/command/PlayerCommands.cxx | 10 +- .../update/{UpdateArchive.cxx => Archive.cxx} | 68 ++++----- .../{UpdateContainer.cxx => Container.cxx} | 33 ++--- .../update/{UpdateDatabase.cxx => Editor.cxx} | 26 ++-- .../update/{UpdateDatabase.hxx => Editor.hxx} | 48 ++++--- src/db/update/InotifyQueue.cxx | 4 +- src/db/update/InotifyQueue.hxx | 7 +- src/db/update/InotifyUpdate.cxx | 4 +- src/db/update/InotifyUpdate.hxx | 7 +- src/db/update/{UpdateQueue.cxx => Queue.cxx} | 13 +- src/db/update/{UpdateQueue.hxx => Queue.hxx} | 15 +- .../update/{UpdateRemove.cxx => Remove.cxx} | 21 +-- .../update/{UpdateRemove.hxx => Remove.hxx} | 35 +++-- .../{UpdateInternal.hxx => Service.cxx} | 11 +- src/db/update/Service.hxx | 89 ++++++++++++ src/db/update/UpdateArchive.hxx | 50 ------- src/db/update/UpdateContainer.hxx | 36 ----- src/db/update/UpdateGlue.cxx | 82 ++++------- src/db/update/UpdateGlue.hxx | 43 ------ src/db/update/UpdateSong.cxx | 29 ++-- src/db/update/UpdateSong.hxx | 34 ----- src/db/update/UpdateWalk.hxx | 37 ----- src/db/update/{UpdateWalk.cxx => Walk.cxx} | 136 ++++++++---------- src/db/update/Walk.hxx | 134 +++++++++++++++++ 29 files changed, 514 insertions(+), 520 deletions(-) rename src/db/update/{UpdateArchive.cxx => Archive.cxx} (75%) rename src/db/update/{UpdateContainer.cxx => Container.cxx} (79%) rename src/db/update/{UpdateDatabase.cxx => Editor.cxx} (82%) rename src/db/update/{UpdateDatabase.hxx => Editor.hxx} (59%) rename src/db/update/{UpdateQueue.cxx => Queue.cxx} (80%) rename src/db/update/{UpdateQueue.hxx => Queue.hxx} (81%) rename src/db/update/{UpdateRemove.cxx => Remove.cxx} (86%) rename src/db/update/{UpdateRemove.hxx => Remove.hxx} (54%) rename src/db/update/{UpdateInternal.hxx => Service.cxx} (85%) create mode 100644 src/db/update/Service.hxx delete mode 100644 src/db/update/UpdateArchive.hxx delete mode 100644 src/db/update/UpdateContainer.hxx delete mode 100644 src/db/update/UpdateGlue.hxx delete mode 100644 src/db/update/UpdateSong.hxx delete mode 100644 src/db/update/UpdateWalk.hxx rename src/db/update/{UpdateWalk.cxx => Walk.cxx} (77%) create mode 100644 src/db/update/Walk.hxx diff --git a/Makefile.am b/Makefile.am index c51a12e6b..a8850ce29 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,15 +124,15 @@ src_mpd_SOURCES = \ src/filter/FilterInternal.hxx \ src/filter/FilterRegistry.cxx src/filter/FilterRegistry.hxx \ src/db/update/UpdateDomain.cxx src/db/update/UpdateDomain.hxx \ - src/db/update/UpdateGlue.cxx src/db/update/UpdateGlue.hxx \ - src/db/update/UpdateQueue.cxx src/db/update/UpdateQueue.hxx \ + src/db/update/Service.cxx src/db/update/Service.hxx \ + src/db/update/UpdateGlue.cxx \ + src/db/update/Queue.cxx src/db/update/Queue.hxx \ src/db/update/UpdateIO.cxx src/db/update/UpdateIO.hxx \ - src/db/update/UpdateDatabase.cxx src/db/update/UpdateDatabase.hxx \ - src/db/update/UpdateWalk.cxx src/db/update/UpdateWalk.hxx \ - src/db/update/UpdateSong.cxx src/db/update/UpdateSong.hxx \ - src/db/update/UpdateContainer.cxx src/db/update/UpdateContainer.hxx \ - src/db/update/UpdateInternal.hxx \ - src/db/update/UpdateRemove.cxx src/db/update/UpdateRemove.hxx \ + src/db/update/Editor.cxx src/db/update/Editor.hxx \ + src/db/update/Walk.cxx src/db/update/Walk.hxx \ + src/db/update/UpdateSong.cxx \ + src/db/update/Container.cxx \ + src/db/update/Remove.cxx src/db/update/Remove.hxx \ src/db/update/ExcludeList.cxx src/db/update/ExcludeList.hxx \ src/client/Client.cxx src/client/Client.hxx \ src/client/ClientInternal.hxx \ @@ -486,7 +486,7 @@ if ENABLE_ARCHIVE noinst_LIBRARIES += libarchive.a src_mpd_SOURCES += \ - src/db/update/UpdateArchive.cxx src/db/update/UpdateArchive.hxx + src/db/update/Archive.cxx libarchive_a_SOURCES = \ src/archive/ArchiveDomain.cxx src/archive/ArchiveDomain.hxx \ diff --git a/src/GlobalEvents.hxx b/src/GlobalEvents.hxx index 106eeb8f8..98aa4850f 100644 --- a/src/GlobalEvents.hxx +++ b/src/GlobalEvents.hxx @@ -33,9 +33,6 @@ class EventLoop; namespace GlobalEvents { enum Event { - /** database update was finished */ - UPDATE, - /** during database update, a song was deleted */ DELETE, diff --git a/src/Instance.hxx b/src/Instance.hxx index a14719839..d67ff9a51 100644 --- a/src/Instance.hxx +++ b/src/Instance.hxx @@ -29,6 +29,7 @@ class NeighborGlue; #endif +class UpdateService; class ClientList; struct Partition; @@ -42,6 +43,8 @@ struct Instance final NeighborGlue *neighbors; #endif + UpdateService *update; + ClientList *client_list; Partition *partition; diff --git a/src/Main.cxx b/src/Main.cxx index ab70c875c..2c5d183ea 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -23,7 +23,7 @@ #include "CommandLine.hxx" #include "PlaylistFile.hxx" #include "PlaylistGlobal.hxx" -#include "db/update/UpdateGlue.hxx" +#include "db/update/Service.hxx" #include "MusicChunk.hxx" #include "StateFile.hxx" #include "PlayerThread.hxx" @@ -448,10 +448,13 @@ int mpd_main(int argc, char *argv[]) } decoder_plugin_init_all(); - update_global_init(); const bool create_db = !glue_db_init_and_load(); + instance->update = db_is_simple() + ? new UpdateService(*main_loop) + : nullptr; + glue_sticker_init(); command_init(); @@ -490,7 +493,7 @@ int mpd_main(int argc, char *argv[]) if (create_db) { /* the database failed to load: recreate the database */ - unsigned job = update_enqueue("", true); + unsigned job = instance->update->Enqueue("", true); if (job == 0) FatalError("directory update failed"); } @@ -504,8 +507,9 @@ int mpd_main(int argc, char *argv[]) if (config_get_bool(CONF_AUTO_UPDATE, false)) { #ifdef ENABLE_INOTIFY - if (mapper_has_music_directory()) - mpd_inotify_init(*main_loop, + if (mapper_has_music_directory() && + instance->update != nullptr) + mpd_inotify_init(*main_loop, *instance->update, config_get_unsigned(CONF_AUTO_UPDATE_DEPTH, G_MAXUINT)); #else @@ -558,6 +562,8 @@ int mpd_main(int argc, char *argv[]) } #endif + delete instance->update; + const clock_t start = clock(); DatabaseGlobalDeinit(); FormatDebug(main_domain, @@ -575,7 +581,6 @@ int mpd_main(int argc, char *argv[]) mapper_finish(); delete instance->partition; command_finish(); - update_global_finish(); decoder_plugin_deinit_all(); #ifdef ENABLE_ARCHIVE archive_plugin_deinit_all(); diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index f8976e561..2aa67f22c 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "OtherCommands.hxx" #include "DatabaseCommands.hxx" -#include "db/update/UpdateGlue.hxx" +#include "db/update/Service.hxx" #include "CommandError.hxx" #include "db/Uri.hxx" #include "DetachedSong.hxx" @@ -46,6 +46,7 @@ #include "client/ClientFile.hxx" #include "client/Client.hxx" #include "Partition.hxx" +#include "Instance.hxx" #include "Idle.hxx" #include @@ -186,7 +187,6 @@ CommandResult handle_update(Client &client, gcc_unused int argc, char *argv[]) { const char *path = ""; - unsigned ret; assert(argc <= 2); if (argc == 2) { @@ -202,7 +202,13 @@ handle_update(Client &client, gcc_unused int argc, char *argv[]) } } - ret = update_enqueue(path, false); + UpdateService *update = client.partition.instance.update; + if (update == nullptr) { + command_error(client, ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; + } + + unsigned ret = update->Enqueue(path, false); if (ret > 0) { client_printf(client, "updating_db: %i\n", ret); return CommandResult::OK; @@ -217,7 +223,6 @@ CommandResult handle_rescan(Client &client, gcc_unused int argc, char *argv[]) { const char *path = ""; - unsigned ret; assert(argc <= 2); if (argc == 2) { @@ -230,7 +235,13 @@ handle_rescan(Client &client, gcc_unused int argc, char *argv[]) } } - ret = update_enqueue(path, true); + UpdateService *update = client.partition.instance.update; + if (update == nullptr) { + command_error(client, ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; + } + + unsigned ret = update->Enqueue(path, true); if (ret > 0) { client_printf(client, "updating_db: %i\n", ret); return CommandResult::OK; diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index 5dc55b485..d51dbdc3d 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -22,10 +22,11 @@ #include "CommandError.hxx" #include "Playlist.hxx" #include "PlaylistPrint.hxx" -#include "db/update/UpdateGlue.hxx" +#include "db/update/Service.hxx" #include "client/Client.hxx" #include "mixer/Volume.hxx" #include "Partition.hxx" +#include "Instance.hxx" #include "protocol/Result.hxx" #include "protocol/ArgParser.hxx" #include "AudioFormat.hxx" @@ -111,7 +112,6 @@ handle_status(Client &client, gcc_unused int argc, gcc_unused char *argv[]) { const char *state = nullptr; - int updateJobId; int song; const auto player_status = client.player_control.GetStatus(); @@ -187,7 +187,11 @@ handle_status(Client &client, } } - if ((updateJobId = isUpdatingDB())) { + const UpdateService *update_service = client.partition.instance.update; + unsigned updateJobId = update_service != nullptr + ? update_service->GetId() + : 0; + if (updateJobId != 0) { client_printf(client, COMMAND_STATUS_UPDATING_DB ": %i\n", updateJobId); diff --git a/src/db/update/UpdateArchive.cxx b/src/db/update/Archive.cxx similarity index 75% rename from src/db/update/UpdateArchive.cxx rename to src/db/update/Archive.cxx index 5e733202d..6d47ae0d1 100644 --- a/src/db/update/UpdateArchive.cxx +++ b/src/db/update/Archive.cxx @@ -18,8 +18,7 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateArchive.hxx" -#include "UpdateInternal.hxx" +#include "Walk.hxx" #include "UpdateDomain.hxx" #include "db/DatabaseLock.hxx" #include "db/Directory.hxx" @@ -35,10 +34,11 @@ #include +#include #include -static void -update_archive_tree(Directory &directory, const char *name) +void +UpdateWalk::UpdateArchiveTree(Directory &directory, const char *name) { const char *tmp = strchr(name, '/'); if (tmp) { @@ -51,7 +51,7 @@ update_archive_tree(Directory &directory, const char *name) db_unlock(); //create directories first - update_archive_tree(*subdir, tmp+1); + UpdateArchiveTree(*subdir, tmp + 1); } else { if (strlen(name) == 0) { LogWarning(update_domain, @@ -78,6 +78,21 @@ update_archive_tree(Directory &directory, const char *name) } } +class UpdateArchiveVisitor final : public ArchiveVisitor { + UpdateWalk &walk; + Directory *directory; + + public: + UpdateArchiveVisitor(UpdateWalk &_walk, Directory *_directory) + :walk(_walk), directory(_directory) {} + + virtual void VisitArchiveEntry(const char *path_utf8) override { + FormatDebug(update_domain, + "adding archive file: %s", path_utf8); + walk.UpdateArchiveTree(*directory, path_utf8); + } +}; + /** * Updates the file listing from an archive file. * @@ -86,10 +101,10 @@ update_archive_tree(Directory &directory, const char *name) * @param st stat() information on the archive file * @param plugin the archive plugin which fits this archive type */ -static void -update_archive_file2(Directory &parent, const char *name, - const struct stat *st, - const struct archive_plugin *plugin) +void +UpdateWalk::UpdateArchiveFile(Directory &parent, const char *name, + const struct stat *st, + const archive_plugin &plugin) { db_lock(); Directory *directory = parent.FindChild(name); @@ -105,7 +120,7 @@ update_archive_file2(Directory &parent, const char *name, /* open archive */ Error error; - ArchiveFile *file = archive_file_open(plugin, path_fs.c_str(), error); + ArchiveFile *file = archive_file_open(&plugin, path_fs.c_str(), error); if (file == nullptr) { LogError(error); return; @@ -126,44 +141,21 @@ update_archive_file2(Directory &parent, const char *name, directory->mtime = st->st_mtime; - class UpdateArchiveVisitor final : public ArchiveVisitor { - Directory *directory; - - public: - UpdateArchiveVisitor(Directory *_directory) - :directory(_directory) {} - - virtual void VisitArchiveEntry(const char *path_utf8) override { - FormatDebug(update_domain, - "adding archive file: %s", path_utf8); - update_archive_tree(*directory, path_utf8); - } - }; - - UpdateArchiveVisitor visitor(directory); + UpdateArchiveVisitor visitor(*this, directory); file->Visit(visitor); file->Close(); } bool -update_archive_file(Directory &directory, - const char *name, const char *suffix, - const struct stat *st) +UpdateWalk::UpdateArchiveFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st) { -#ifdef ENABLE_ARCHIVE const struct archive_plugin *plugin = archive_plugin_from_suffix(suffix); if (plugin == nullptr) return false; - update_archive_file2(directory, name, st, plugin); + UpdateArchiveFile(directory, name, st, *plugin); return true; -#else - (void)directory; - (void)name; - (void)suffix; - (void)st; - - return false; -#endif } diff --git a/src/db/update/UpdateContainer.cxx b/src/db/update/Container.cxx similarity index 79% rename from src/db/update/UpdateContainer.cxx rename to src/db/update/Container.cxx index c03d88748..38aacea0a 100644 --- a/src/db/update/UpdateContainer.cxx +++ b/src/db/update/Container.cxx @@ -18,9 +18,7 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateContainer.hxx" -#include "UpdateInternal.hxx" -#include "UpdateDatabase.hxx" +#include "Walk.hxx" #include "UpdateDomain.hxx" #include "db/DatabaseLock.hxx" #include "db/Directory.hxx" @@ -33,19 +31,13 @@ #include "tag/TagBuilder.hxx" #include "Log.hxx" +#include + #include -/** - * Create the specified directory object if it does not exist already - * or if the #stat object indicates that it has been modified since - * the last update. Returns nullptr when it exists already and is - * unmodified. - * - * The caller must lock the database. - */ -static Directory * -make_directory_if_modified(Directory &parent, const char *name, - const struct stat *st) +Directory * +UpdateWalk::MakeDirectoryIfModified(Directory &parent, const char *name, + const struct stat *st) { Directory *directory = parent.FindChild(name); @@ -56,7 +48,7 @@ make_directory_if_modified(Directory &parent, const char *name, return nullptr; } - delete_directory(directory); + editor.DeleteDirectory(directory); modified = true; } @@ -73,10 +65,9 @@ SupportsContainerSuffix(const DecoderPlugin &plugin, const char *suffix) } bool -update_container_file(Directory &directory, - const char *name, - const struct stat *st, - const char *suffix) +UpdateWalk::UpdateContainerFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st) { const DecoderPlugin *_plugin = decoder_plugins_find([suffix](const DecoderPlugin &plugin){ return SupportsContainerSuffix(plugin, suffix); @@ -86,7 +77,7 @@ update_container_file(Directory &directory, const DecoderPlugin &plugin = *_plugin; db_lock(); - Directory *contdir = make_directory_if_modified(directory, name, st); + Directory *contdir = MakeDirectoryIfModified(directory, name, st); if (contdir == nullptr) { /* not modified */ db_unlock(); @@ -128,7 +119,7 @@ update_container_file(Directory &directory, if (tnum == 1) { db_lock(); - delete_directory(contdir); + editor.DeleteDirectory(contdir); db_unlock(); return false; } else diff --git a/src/db/update/UpdateDatabase.cxx b/src/db/update/Editor.cxx similarity index 82% rename from src/db/update/UpdateDatabase.cxx rename to src/db/update/Editor.cxx index 72a68de14..369bad24b 100644 --- a/src/db/update/UpdateDatabase.cxx +++ b/src/db/update/Editor.cxx @@ -18,8 +18,8 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateDatabase.hxx" -#include "UpdateRemove.hxx" +#include "Editor.hxx" +#include "Remove.hxx" #include "db/PlaylistVector.hxx" #include "db/Directory.hxx" #include "db/Song.hxx" @@ -29,7 +29,7 @@ #include void -delete_song(Directory &dir, Song *del) +DatabaseEditor::DeleteSong(Directory &dir, Song *del) { assert(del->parent == &dir); @@ -39,7 +39,7 @@ delete_song(Directory &dir, Song *del) db_unlock(); /* temporary unlock, because update_remove_song() blocks */ /* now take it out of the playlist (in the main_task) */ - update_remove_song(del); + remove.Remove(del); /* finally, all possible references gone, free it */ del->Free(); @@ -53,32 +53,32 @@ delete_song(Directory &dir, Song *del) * * Caller must lock the #db_mutex. */ -static void -clear_directory(Directory &directory) +inline void +DatabaseEditor::ClearDirectory(Directory &directory) { Directory *child, *n; directory_for_each_child_safe(child, n, directory) - delete_directory(child); + DeleteDirectory(child); Song *song, *ns; directory_for_each_song_safe(song, ns, directory) { assert(song->parent == &directory); - delete_song(directory, song); + DeleteSong(directory, song); } } void -delete_directory(Directory *directory) +DatabaseEditor::DeleteDirectory(Directory *directory) { assert(directory->parent != nullptr); - clear_directory(*directory); + ClearDirectory(*directory); directory->Delete(); } bool -delete_name_in(Directory &parent, const char *name) +DatabaseEditor::DeleteNameIn(Directory &parent, const char *name) { bool modified = false; @@ -86,13 +86,13 @@ delete_name_in(Directory &parent, const char *name) Directory *directory = parent.FindChild(name); if (directory != nullptr) { - delete_directory(directory); + DeleteDirectory(directory); modified = true; } Song *song = parent.FindSong(name); if (song != nullptr) { - delete_song(parent, song); + DeleteSong(parent, song); modified = true; } diff --git a/src/db/update/UpdateDatabase.hxx b/src/db/update/Editor.hxx similarity index 59% rename from src/db/update/UpdateDatabase.hxx rename to src/db/update/Editor.hxx index bd7c395f2..a9093d662 100644 --- a/src/db/update/UpdateDatabase.hxx +++ b/src/db/update/Editor.hxx @@ -21,30 +21,40 @@ #define MPD_UPDATE_DATABASE_HXX #include "check.h" +#include "Remove.hxx" struct Directory; struct Song; +class UpdateRemoveService; -/** - * Caller must lock the #db_mutex. - */ -void -delete_song(Directory &parent, Song *song); +class DatabaseEditor final { + UpdateRemoveService remove; -/** - * Recursively free a directory and all its contents. - * - * Caller must lock the #db_mutex. - */ -void -delete_directory(Directory *directory); +public: + DatabaseEditor(EventLoop &_loop) + :remove(_loop) {} -/** - * Caller must NOT lock the #db_mutex. - * - * @return true if the database was modified - */ -bool -delete_name_in(Directory &parent, const char *name); + /** + * Caller must lock the #db_mutex. + */ + void DeleteSong(Directory &parent, Song *song); + + /** + * Recursively free a directory and all its contents. + * + * Caller must lock the #db_mutex. + */ + void DeleteDirectory(Directory *directory); + + /** + * Caller must NOT lock the #db_mutex. + * + * @return true if the database was modified + */ + bool DeleteNameIn(Directory &parent, const char *name); + +private: + void ClearDirectory(Directory &directory); +}; #endif diff --git a/src/db/update/InotifyQueue.cxx b/src/db/update/InotifyQueue.cxx index f4bccf7ae..013200f98 100644 --- a/src/db/update/InotifyQueue.cxx +++ b/src/db/update/InotifyQueue.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "InotifyQueue.hxx" #include "InotifyDomain.hxx" -#include "UpdateGlue.hxx" +#include "Service.hxx" #include "Log.hxx" #include @@ -40,7 +40,7 @@ InotifyQueue::OnTimeout() while (!queue.empty()) { const char *uri_utf8 = queue.front().c_str(); - id = update_enqueue(uri_utf8, false); + id = update.Enqueue(uri_utf8, false); if (id == 0) { /* retry later */ ScheduleSeconds(INOTIFY_UPDATE_DELAY_S); diff --git a/src/db/update/InotifyQueue.hxx b/src/db/update/InotifyQueue.hxx index 99e2635b1..a9abc2969 100644 --- a/src/db/update/InotifyQueue.hxx +++ b/src/db/update/InotifyQueue.hxx @@ -26,11 +26,16 @@ #include #include +class UpdateService; + class InotifyQueue final : private TimeoutMonitor { + UpdateService &update; + std::list queue; public: - InotifyQueue(EventLoop &_loop):TimeoutMonitor(_loop) {} + InotifyQueue(EventLoop &_loop, UpdateService &_update) + :TimeoutMonitor(_loop), update(_update) {} void Enqueue(const char *uri_utf8); diff --git a/src/db/update/InotifyUpdate.cxx b/src/db/update/InotifyUpdate.cxx index 72f22ceb1..3ba92ebf0 100644 --- a/src/db/update/InotifyUpdate.cxx +++ b/src/db/update/InotifyUpdate.cxx @@ -285,7 +285,7 @@ mpd_inotify_callback(int wd, unsigned mask, } void -mpd_inotify_init(EventLoop &loop, unsigned max_depth) +mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth) { LogDebug(inotify_domain, "initializing inotify"); @@ -320,7 +320,7 @@ mpd_inotify_init(EventLoop &loop, unsigned max_depth) recursive_watch_subdirectories(inotify_root, path, 0); - inotify_queue = new InotifyQueue(loop); + inotify_queue = new InotifyQueue(loop, update); LogDebug(inotify_domain, "watching music directory"); } diff --git a/src/db/update/InotifyUpdate.hxx b/src/db/update/InotifyUpdate.hxx index 7cf486e5a..0260a4ff5 100644 --- a/src/db/update/InotifyUpdate.hxx +++ b/src/db/update/InotifyUpdate.hxx @@ -24,11 +24,12 @@ #include "Compiler.h" class EventLoop; +class UpdateService; #ifdef HAVE_INOTIFY_INIT void -mpd_inotify_init(EventLoop &loop, unsigned max_depth); +mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth); void mpd_inotify_finish(void); @@ -36,7 +37,9 @@ mpd_inotify_finish(void); #else /* !HAVE_INOTIFY_INIT */ static inline void -mpd_inotify_init(gcc_unused EventLoop &loop, gcc_unused unsigned max_depth) +mpd_inotify_init(gcc_unused EventLoop &loop, + gcc_unused UpdateService &update, + gcc_unused unsigned max_depth) { } diff --git a/src/db/update/UpdateQueue.cxx b/src/db/update/Queue.cxx similarity index 80% rename from src/db/update/UpdateQueue.cxx rename to src/db/update/Queue.cxx index a6002f854..4bb0ae725 100644 --- a/src/db/update/UpdateQueue.cxx +++ b/src/db/update/Queue.cxx @@ -18,17 +18,10 @@ */ #include "config.h" -#include "UpdateQueue.hxx" - -#include -#include - -static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32; - -static std::queue> update_queue; +#include "Queue.hxx" bool -update_queue_push(const char *path, bool discard, unsigned id) +UpdateQueue::Push(const char *path, bool discard, unsigned id) { if (update_queue.size() >= MAX_UPDATE_QUEUE_SIZE) return false; @@ -38,7 +31,7 @@ update_queue_push(const char *path, bool discard, unsigned id) } UpdateQueueItem -update_queue_shift() +UpdateQueue::Pop() { if (update_queue.empty()) return UpdateQueueItem(); diff --git a/src/db/update/UpdateQueue.hxx b/src/db/update/Queue.hxx similarity index 81% rename from src/db/update/UpdateQueue.hxx rename to src/db/update/Queue.hxx index e4228f5ed..a5b2aa9ac 100644 --- a/src/db/update/UpdateQueue.hxx +++ b/src/db/update/Queue.hxx @@ -23,6 +23,8 @@ #include "check.h" #include +#include +#include struct UpdateQueueItem { std::string path_utf8; @@ -39,10 +41,15 @@ struct UpdateQueueItem { } }; -bool -update_queue_push(const char *path, bool discard, unsigned id); +class UpdateQueue { + static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32; -UpdateQueueItem -update_queue_shift(); + std::queue> update_queue; + +public: + bool Push(const char *path, bool discard, unsigned id); + + UpdateQueueItem Pop(); +}; #endif diff --git a/src/db/update/UpdateRemove.cxx b/src/db/update/Remove.cxx similarity index 86% rename from src/db/update/UpdateRemove.cxx rename to src/db/update/Remove.cxx index c57758aef..e2c172a4b 100644 --- a/src/db/update/UpdateRemove.cxx +++ b/src/db/update/Remove.cxx @@ -18,7 +18,7 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateRemove.hxx" +#include "Remove.hxx" #include "UpdateDomain.hxx" #include "GlobalEvents.hxx" #include "thread/Mutex.hxx" @@ -36,17 +36,12 @@ #include -static const Song *removed_song; - -static Mutex remove_mutex; -static Cond remove_cond; - /** * Safely remove a song from the database. This must be done in the * main task, to be sure that there is no pointer left to it. */ -static void -song_remove_event(void) +void +UpdateRemoveService::RunDeferred() { assert(removed_song != nullptr); @@ -74,19 +69,13 @@ song_remove_event(void) } void -update_remove_global_init(void) -{ - GlobalEvents::Register(GlobalEvents::DELETE, song_remove_event); -} - -void -update_remove_song(const Song *song) +UpdateRemoveService::Remove(const Song *song) { assert(removed_song == nullptr); removed_song = song; - GlobalEvents::Emit(GlobalEvents::DELETE); + DeferredMonitor::Schedule(); remove_mutex.lock(); diff --git a/src/db/update/UpdateRemove.hxx b/src/db/update/Remove.hxx similarity index 54% rename from src/db/update/UpdateRemove.hxx rename to src/db/update/Remove.hxx index d54e3aa80..1970c0a07 100644 --- a/src/db/update/UpdateRemove.hxx +++ b/src/db/update/Remove.hxx @@ -21,18 +21,37 @@ #define MPD_UPDATE_REMOVE_HXX #include "check.h" +#include "event/DeferredMonitor.hxx" +#include "thread/Mutex.hxx" +#include "thread/Cond.hxx" struct Song; -void -update_remove_global_init(void); - /** - * Sends a signal to the main thread which will in turn remove the - * song: from the sticker database and from the playlist. This - * serialized access is implemented to avoid excessive locking. + * This class handles #Song removal. It defers the action to the main + * thread to ensure that all references to the #Song are gone. */ -void -update_remove_song(const Song *song); +class UpdateRemoveService final : DeferredMonitor { + Mutex remove_mutex; + Cond remove_cond; + + const Song *removed_song; + +public: + UpdateRemoveService(EventLoop &_loop) + :DeferredMonitor(_loop) {} + + /** + * Sends a signal to the main thread which will in turn remove + * the song: from the sticker database and from the playlist. + * This serialized access is implemented to avoid excessive + * locking. + */ + void Remove(const Song *song); + +private: + /* virtual methods from class DeferredMonitor */ + virtual void RunDeferred() override; +}; #endif diff --git a/src/db/update/UpdateInternal.hxx b/src/db/update/Service.cxx similarity index 85% rename from src/db/update/UpdateInternal.hxx rename to src/db/update/Service.cxx index 2e373bd06..cc2a86968 100644 --- a/src/db/update/UpdateInternal.hxx +++ b/src/db/update/Service.cxx @@ -17,12 +17,5 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_UPDATE_INTERNAL_H -#define MPD_UPDATE_INTERNAL_H - -#include "check.h" - -extern bool walk_discard; -extern bool modified; - -#endif +#include "config.h" +#include "Service.hxx" diff --git a/src/db/update/Service.hxx b/src/db/update/Service.hxx new file mode 100644 index 000000000..3ea4baea1 --- /dev/null +++ b/src/db/update/Service.hxx @@ -0,0 +1,89 @@ +/* + * 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_UPDATE_SERVICE_HXX +#define MPD_UPDATE_SERVICE_HXX + +#include "check.h" +#include "Queue.hxx" +#include "Walk.hxx" +#include "event/DeferredMonitor.hxx" +#include "thread/Thread.hxx" + +/** + * This class manages the update queue and runs the update thread. + */ +class UpdateService final : DeferredMonitor { + enum Progress { + UPDATE_PROGRESS_IDLE = 0, + UPDATE_PROGRESS_RUNNING = 1, + UPDATE_PROGRESS_DONE = 2 + }; + + Progress progress; + + bool modified; + + Thread update_thread; + + static const unsigned update_task_id_max = 1 << 15; + + unsigned update_task_id; + + UpdateQueue queue; + + UpdateQueueItem next; + + UpdateWalk walk; + +public: + UpdateService(EventLoop &_loop); + + /** + * Returns a non-zero job id when we are currently updating + * the database. + */ + unsigned GetId() const { + return next.id; + } + + /** + * Add this path to the database update queue. + * + * @param path a path to update; if an empty string, + * the whole music directory is updated + * @return the job id, or 0 on error + */ + gcc_nonnull_all + unsigned Enqueue(const char *path, bool discard); + +private: + /* virtual methods from class DeferredMonitor */ + virtual void RunDeferred() override; + + /* the update thread */ + void Task(); + static void Task(void *ctx); + + void StartThread(UpdateQueueItem &&i); + + unsigned GenerateId(); +}; + +#endif diff --git a/src/db/update/UpdateArchive.hxx b/src/db/update/UpdateArchive.hxx deleted file mode 100644 index 1fc9af349..000000000 --- a/src/db/update/UpdateArchive.hxx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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_UPDATE_ARCHIVE_HXX -#define MPD_UPDATE_ARCHIVE_HXX - -#include "check.h" -#include "Compiler.h" - -#include - -struct Directory; - -#ifdef ENABLE_ARCHIVE - -bool -update_archive_file(Directory &directory, - const char *name, const char *suffix, - const struct stat *st); - -#else - -static inline bool -update_archive_file(gcc_unused Directory &directory, - gcc_unused const char *name, - gcc_unused const char *suffix, - gcc_unused const struct stat *st) -{ - return false; -} - -#endif - -#endif diff --git a/src/db/update/UpdateContainer.hxx b/src/db/update/UpdateContainer.hxx deleted file mode 100644 index 8125f71ee..000000000 --- a/src/db/update/UpdateContainer.hxx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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_UPDATE_CONTAINER_HXX -#define MPD_UPDATE_CONTAINER_HXX - -#include "check.h" - -#include - -struct Directory; -struct DecoderPlugin; - -bool -update_container_file(Directory &directory, - const char *name, - const struct stat *st, - const char *suffix); - -#endif diff --git a/src/db/update/UpdateGlue.cxx b/src/db/update/UpdateGlue.cxx index d18747ba1..2214bb2f7 100644 --- a/src/db/update/UpdateGlue.cxx +++ b/src/db/update/UpdateGlue.cxx @@ -18,15 +18,11 @@ */ #include "config.h" -#include "UpdateGlue.hxx" -#include "UpdateQueue.hxx" -#include "UpdateWalk.hxx" -#include "UpdateRemove.hxx" +#include "Service.hxx" #include "UpdateDomain.hxx" #include "Mapper.hxx" #include "db/DatabaseSimple.hxx" #include "Idle.hxx" -#include "GlobalEvents.hxx" #include "util/Error.hxx" #include "Log.hxx" #include "Main.hxx" @@ -38,30 +34,8 @@ #include -static enum update_progress { - UPDATE_PROGRESS_IDLE = 0, - UPDATE_PROGRESS_RUNNING = 1, - UPDATE_PROGRESS_DONE = 2 -} progress; - -static bool modified; - -static Thread update_thread; - -static const unsigned update_task_id_max = 1 << 15; - -static unsigned update_task_id; - -static UpdateQueueItem next; - -unsigned -isUpdatingDB(void) -{ - return next.id; -} - -static void -update_task(gcc_unused void *ctx) +inline void +UpdateService::Task() { if (!next.path_utf8.empty()) FormatDebug(update_domain, "starting: %s", @@ -71,7 +45,7 @@ update_task(gcc_unused void *ctx) SetThreadIdlePriority(); - modified = update_walk(next.path_utf8.c_str(), next.discard); + modified = walk.Walk(next.path_utf8.c_str(), next.discard); if (modified || !db_exists()) { Error error; @@ -86,11 +60,18 @@ update_task(gcc_unused void *ctx) LogDebug(update_domain, "finished"); progress = UPDATE_PROGRESS_DONE; - GlobalEvents::Emit(GlobalEvents::UPDATE); + DeferredMonitor::Schedule(); } -static void -spawn_update_task(UpdateQueueItem &&i) +void +UpdateService::Task(void *ctx) +{ + UpdateService &service = *(UpdateService *)ctx; + return service.Task(); +} + +void +UpdateService::StartThread(UpdateQueueItem &&i) { assert(main_thread.IsInside()); @@ -100,15 +81,15 @@ spawn_update_task(UpdateQueueItem &&i) next = std::move(i); Error error; - if (!update_thread.Start(update_task, nullptr, error)) + if (!update_thread.Start(Task, this, error)) FatalError(error); FormatDebug(update_domain, "spawned thread for update job id %i", next.id); } -static unsigned -generate_update_id() +unsigned +UpdateService::GenerateId() { unsigned id = update_task_id + 1; if (id > update_task_id_max) @@ -117,7 +98,7 @@ generate_update_id() } unsigned -update_enqueue(const char *path, bool discard) +UpdateService::Enqueue(const char *path, bool discard) { assert(main_thread.IsInside()); @@ -125,16 +106,16 @@ update_enqueue(const char *path, bool discard) return 0; if (progress != UPDATE_PROGRESS_IDLE) { - const unsigned id = generate_update_id(); - if (!update_queue_push(path, discard, id)) + const unsigned id = GenerateId(); + if (!queue.Push(path, discard, id)) return 0; update_task_id = id; return id; } - const unsigned id = update_task_id = generate_update_id(); - spawn_update_task(UpdateQueueItem(path, discard, id)); + const unsigned id = update_task_id = GenerateId(); + StartThread(UpdateQueueItem(path, discard, id)); idle_add(IDLE_UPDATE); @@ -144,7 +125,8 @@ update_enqueue(const char *path, bool discard) /** * Called in the main thread after the database update is finished. */ -static void update_finished_event(void) +void +UpdateService::RunDeferred() { assert(progress == UPDATE_PROGRESS_DONE); assert(next.IsDefined()); @@ -158,24 +140,16 @@ static void update_finished_event(void) /* send "idle" events */ instance->DatabaseModified(); - auto i = update_queue_shift(); + auto i = queue.Pop(); if (i.IsDefined()) { /* schedule the next path */ - spawn_update_task(std::move(i)); + StartThread(std::move(i)); } else { progress = UPDATE_PROGRESS_IDLE; } } -void update_global_init(void) +UpdateService::UpdateService(EventLoop &_loop) + :DeferredMonitor(_loop), walk(_loop) { - GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event); - - update_remove_global_init(); - update_walk_global_init(); -} - -void update_global_finish(void) -{ - update_walk_global_finish(); } diff --git a/src/db/update/UpdateGlue.hxx b/src/db/update/UpdateGlue.hxx deleted file mode 100644 index 6e247414e..000000000 --- a/src/db/update/UpdateGlue.hxx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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_UPDATE_GLUE_HXX -#define MPD_UPDATE_GLUE_HXX - -#include "Compiler.h" - -void update_global_init(void); - -void update_global_finish(void); - -unsigned -isUpdatingDB(void); - -/** - * Add this path to the database update queue. - * - * @param path a path to update; if an empty string, - * the whole music directory is updated - * @return the job id, or 0 on error - */ -gcc_nonnull_all -unsigned -update_enqueue(const char *path, bool discard); - -#endif diff --git a/src/db/update/UpdateSong.cxx b/src/db/update/UpdateSong.cxx index ac2d01cd2..afd609f9a 100644 --- a/src/db/update/UpdateSong.cxx +++ b/src/db/update/UpdateSong.cxx @@ -18,11 +18,8 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateSong.hxx" -#include "UpdateInternal.hxx" +#include "Service.hxx" #include "UpdateIO.hxx" -#include "UpdateDatabase.hxx" -#include "UpdateContainer.hxx" #include "UpdateDomain.hxx" #include "db/DatabaseLock.hxx" #include "db/Directory.hxx" @@ -32,10 +29,10 @@ #include -static void -update_song_file2(Directory &directory, - const char *name, const struct stat *st, - const char *suffix) +inline void +UpdateWalk::UpdateSongFile2(Directory &directory, + const char *name, const char *suffix, + const struct stat *st) { db_lock(); Song *song = directory.FindSong(name); @@ -47,7 +44,7 @@ update_song_file2(Directory &directory, directory.GetPath(), name); if (song != nullptr) { db_lock(); - delete_song(directory, song); + editor.DeleteSong(directory, song); db_unlock(); } @@ -56,10 +53,10 @@ update_song_file2(Directory &directory, if (!(song != nullptr && st->st_mtime == song->mtime && !walk_discard) && - update_container_file(directory, name, st, suffix)) { + UpdateContainerFile(directory, name, suffix, st)) { if (song != nullptr) { db_lock(); - delete_song(directory, song); + editor.DeleteSong(directory, song); db_unlock(); } @@ -92,7 +89,7 @@ update_song_file2(Directory &directory, "deleting unrecognized file %s/%s", directory.GetPath(), name); db_lock(); - delete_song(directory, song); + editor.DeleteSong(directory, song); db_unlock(); } @@ -101,13 +98,13 @@ update_song_file2(Directory &directory, } bool -update_song_file(Directory &directory, - const char *name, const char *suffix, - const struct stat *st) +UpdateWalk::UpdateSongFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st) { if (!decoder_plugins_supports_suffix(suffix)) return false; - update_song_file2(directory, name, st, suffix); + UpdateSongFile2(directory, name, suffix, st); return true; } diff --git a/src/db/update/UpdateSong.hxx b/src/db/update/UpdateSong.hxx deleted file mode 100644 index 5feb01928..000000000 --- a/src/db/update/UpdateSong.hxx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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_UPDATE_SONG_HXX -#define MPD_UPDATE_SONG_HXX - -#include "check.h" - -#include - -struct Directory; - -bool -update_song_file(Directory &directory, - const char *name, const char *suffix, - const struct stat *st); - -#endif diff --git a/src/db/update/UpdateWalk.hxx b/src/db/update/UpdateWalk.hxx deleted file mode 100644 index e908829e3..000000000 --- a/src/db/update/UpdateWalk.hxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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_UPDATE_WALK_HXX -#define MPD_UPDATE_WALK_HXX - -#include "check.h" - -void -update_walk_global_init(void); - -void -update_walk_global_finish(void); - -/** - * Returns true if the database was modified. - */ -bool -update_walk(const char *path, bool discard); - -#endif diff --git a/src/db/update/UpdateWalk.cxx b/src/db/update/Walk.cxx similarity index 77% rename from src/db/update/UpdateWalk.cxx rename to src/db/update/Walk.cxx index 95c7e6a81..5a799d8f7 100644 --- a/src/db/update/UpdateWalk.cxx +++ b/src/db/update/Walk.cxx @@ -18,11 +18,9 @@ */ #include "config.h" /* must be first for large file support */ -#include "UpdateWalk.hxx" +#include "Walk.hxx" #include "UpdateIO.hxx" -#include "UpdateDatabase.hxx" -#include "UpdateSong.hxx" -#include "UpdateArchive.hxx" +#include "Editor.hxx" #include "UpdateDomain.hxx" #include "db/DatabaseLock.hxx" #include "db/DatabaseSimple.hxx" @@ -49,21 +47,8 @@ #include #include -bool walk_discard; -bool modified; - -#ifndef WIN32 - -static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true; -static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true; - -static bool follow_inside_symlinks; -static bool follow_outside_symlinks; - -#endif - -void -update_walk_global_init(void) +UpdateWalk::UpdateWalk(EventLoop &_loop) + :editor(_loop) { #ifndef WIN32 follow_inside_symlinks = @@ -76,11 +61,6 @@ update_walk_global_init(void) #endif } -void -update_walk_global_finish(void) -{ -} - static void directory_set_stat(Directory &dir, const struct stat *st) { @@ -89,9 +69,9 @@ directory_set_stat(Directory &dir, const struct stat *st) dir.have_stat = true; } -static void -remove_excluded_from_directory(Directory &directory, - const ExcludeList &exclude_list) +inline void +UpdateWalk::RemoveExcludedFromDirectory(Directory &directory, + const ExcludeList &exclude_list) { db_lock(); @@ -100,7 +80,7 @@ remove_excluded_from_directory(Directory &directory, const auto name_fs = AllocatedPath::FromUTF8(child->GetName()); if (name_fs.IsNull() || exclude_list.Check(name_fs)) { - delete_directory(child); + editor.DeleteDirectory(child); modified = true; } } @@ -111,7 +91,7 @@ remove_excluded_from_directory(Directory &directory, const auto name_fs = AllocatedPath::FromUTF8(song->uri); if (name_fs.IsNull() || exclude_list.Check(name_fs)) { - delete_song(directory, song); + editor.DeleteSong(directory, song); modified = true; } } @@ -119,8 +99,8 @@ remove_excluded_from_directory(Directory &directory, db_unlock(); } -static void -purge_deleted_from_directory(Directory &directory) +inline void +UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) { Directory *child, *n; directory_for_each_child_safe(child, n, directory) { @@ -128,7 +108,7 @@ purge_deleted_from_directory(Directory &directory) continue; db_lock(); - delete_directory(child); + editor.DeleteDirectory(child); db_unlock(); modified = true; @@ -139,7 +119,7 @@ purge_deleted_from_directory(Directory &directory) const auto path = map_song_fs(*song); if (path.IsNull() || !FileExists(path)) { db_lock(); - delete_song(directory, song); + editor.DeleteSong(directory, song); db_unlock(); modified = true; @@ -195,10 +175,10 @@ find_inode_ancestor(Directory *parent, ino_t inode, dev_t device) return 0; } -static bool -update_playlist_file2(Directory &directory, - const char *name, const char *suffix, - const struct stat *st) +inline bool +UpdateWalk::UpdatePlaylistFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st) { if (!playlist_suffix_supported(suffix)) return false; @@ -212,30 +192,27 @@ update_playlist_file2(Directory &directory, return true; } -static bool -update_regular_file(Directory &directory, - const char *name, const struct stat *st) +inline bool +UpdateWalk::UpdateRegularFile(Directory &directory, + const char *name, const struct stat *st) { const char *suffix = uri_get_suffix(name); if (suffix == nullptr) return false; - return update_song_file(directory, name, suffix, st) || - update_archive_file(directory, name, suffix, st) || - update_playlist_file2(directory, name, suffix, st); + return UpdateSongFile(directory, name, suffix, st) || + UpdateArchiveFile(directory, name, suffix, st) || + UpdatePlaylistFile(directory, name, suffix, st); } -static bool -update_directory(Directory &directory, const struct stat *st); - -static void -update_directory_child(Directory &directory, - const char *name, const struct stat *st) +void +UpdateWalk::UpdateDirectoryChild(Directory &directory, + const char *name, const struct stat *st) { assert(strchr(name, '/') == nullptr); if (S_ISREG(st->st_mode)) { - update_regular_file(directory, name, st); + UpdateRegularFile(directory, name, st); } else if (S_ISDIR(st->st_mode)) { if (find_inode_ancestor(&directory, st->st_ino, st->st_dev)) return; @@ -246,9 +223,9 @@ update_directory_child(Directory &directory, assert(&directory == subdir->parent); - if (!update_directory(*subdir, st)) { + if (!UpdateDirectory(*subdir, st)) { db_lock(); - delete_directory(subdir); + editor.DeleteDirectory(subdir); db_unlock(); } } else { @@ -268,8 +245,9 @@ static bool skip_path(Path path_fs) } gcc_pure -static bool -skip_symlink(const Directory *directory, const char *utf8_name) +bool +UpdateWalk::SkipSymlink(const Directory *directory, + const char *utf8_name) const { #ifndef WIN32 const auto path_fs = map_directory_child_fs(*directory, utf8_name); @@ -333,8 +311,8 @@ skip_symlink(const Directory *directory, const char *utf8_name) #endif } -static bool -update_directory(Directory &directory, const struct stat *st) +bool +UpdateWalk::UpdateDirectory(Directory &directory, const struct stat *st) { assert(S_ISDIR(st->st_mode)); @@ -358,9 +336,9 @@ update_directory(Directory &directory, const struct stat *st) exclude_list.LoadFile(AllocatedPath::Build(path_fs, ".mpdignore")); if (!exclude_list.IsEmpty()) - remove_excluded_from_directory(directory, exclude_list); + RemoveExcludedFromDirectory(directory, exclude_list); - purge_deleted_from_directory(directory); + PurgeDeletedFromDirectory(directory); while (reader.ReadEntry()) { std::string utf8; @@ -375,15 +353,15 @@ update_directory(Directory &directory, const struct stat *st) if (utf8.empty()) continue; - if (skip_symlink(&directory, utf8.c_str())) { - modified |= delete_name_in(directory, utf8.c_str()); + if (SkipSymlink(&directory, utf8.c_str())) { + modified |= editor.DeleteNameIn(directory, utf8.c_str()); continue; } if (stat_directory_child(directory, utf8.c_str(), &st2) == 0) - update_directory_child(directory, utf8.c_str(), &st2); + UpdateDirectoryChild(directory, utf8.c_str(), &st2); else - modified |= delete_name_in(directory, utf8.c_str()); + modified |= editor.DeleteNameIn(directory, utf8.c_str()); } directory.mtime = st->st_mtime; @@ -391,8 +369,8 @@ update_directory(Directory &directory, const struct stat *st) return true; } -static Directory * -directory_make_child_checked(Directory &parent, const char *name_utf8) +inline Directory * +UpdateWalk::DirectoryMakeChildChecked(Directory &parent, const char *name_utf8) { db_lock(); Directory *directory = parent.FindChild(name_utf8); @@ -406,7 +384,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8) find_inode_ancestor(&parent, st.st_ino, st.st_dev)) return nullptr; - if (skip_symlink(&parent, name_utf8)) + if (SkipSymlink(&parent, name_utf8)) return nullptr; /* if we're adding directory paths, make sure to delete filenames @@ -414,7 +392,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8) db_lock(); Song *conflicting = parent.FindSong(name_utf8); if (conflicting) - delete_song(parent, conflicting); + editor.DeleteSong(parent, conflicting); directory = parent.CreateChild(name_utf8); db_unlock(); @@ -423,8 +401,8 @@ directory_make_child_checked(Directory &parent, const char *name_utf8) return directory; } -static Directory * -directory_make_uri_parent_checked(const char *uri) +inline Directory * +UpdateWalk::DirectoryMakeUriParentChecked(const char *uri) { Directory *directory = db_get_root(); char *duplicated = xstrdup(uri); @@ -436,8 +414,8 @@ directory_make_uri_parent_checked(const char *uri) if (*name_utf8 == 0) continue; - directory = directory_make_child_checked(*directory, - name_utf8); + directory = DirectoryMakeChildChecked(*directory, + name_utf8); if (directory == nullptr) break; @@ -448,37 +426,37 @@ directory_make_uri_parent_checked(const char *uri) return directory; } -static void -update_uri(const char *uri) +inline void +UpdateWalk::UpdateUri(const char *uri) { - Directory *parent = directory_make_uri_parent_checked(uri); + Directory *parent = DirectoryMakeUriParentChecked(uri); if (parent == nullptr) return; const char *name = PathTraitsUTF8::GetBase(uri); struct stat st; - if (!skip_symlink(parent, name) && + if (!SkipSymlink(parent, name) && stat_directory_child(*parent, name, &st) == 0) - update_directory_child(*parent, name, &st); + UpdateDirectoryChild(*parent, name, &st); else - modified |= delete_name_in(*parent, name); + modified |= editor.DeleteNameIn(*parent, name); } bool -update_walk(const char *path, bool discard) +UpdateWalk::Walk(const char *path, bool discard) { walk_discard = discard; modified = false; if (path != nullptr && !isRootDirectory(path)) { - update_uri(path); + UpdateUri(path); } else { Directory *directory = db_get_root(); struct stat st; if (stat_directory(*directory, &st) == 0) - update_directory(*directory, &st); + UpdateDirectory(*directory, &st); } return modified; diff --git a/src/db/update/Walk.hxx b/src/db/update/Walk.hxx new file mode 100644 index 000000000..12274ccdf --- /dev/null +++ b/src/db/update/Walk.hxx @@ -0,0 +1,134 @@ +/* + * 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_UPDATE_WALK_HXX +#define MPD_UPDATE_WALK_HXX + +#include "check.h" +#include "Editor.hxx" + +#include + +struct stat; +struct Directory; +struct archive_plugin; +class ExcludeList; + +class UpdateWalk final { +#ifdef ENABLE_ARCHIVE + friend class UpdateArchiveVisitor; +#endif + +#ifndef WIN32 + static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true; + static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true; + + bool follow_inside_symlinks; + bool follow_outside_symlinks; +#endif + + bool walk_discard; + bool modified; + + DatabaseEditor editor; + +public: + UpdateWalk(EventLoop &_loop); + + /** + * Returns true if the database was modified. + */ + bool Walk(const char *path, bool discard); + +private: + gcc_pure + bool SkipSymlink(const Directory *directory, + const char *utf8_name) const; + + void RemoveExcludedFromDirectory(Directory &directory, + const ExcludeList &exclude_list); + + void PurgeDeletedFromDirectory(Directory &directory); + + void UpdateSongFile2(Directory &directory, + const char *name, const char *suffix, + const struct stat *st); + + bool UpdateSongFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st); + + bool UpdateContainerFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st); + + +#ifdef ENABLE_ARCHIVE + void UpdateArchiveTree(Directory &parent, const char *name); + + bool UpdateArchiveFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st); + + void UpdateArchiveFile(Directory &directory, const char *name, + const struct stat *st, + const archive_plugin &plugin); + + +#else + bool UpdateArchiveFile(gcc_unused Directory &directory, + gcc_unused const char *name, + gcc_unused const char *suffix, + gcc_unused const struct stat *st) { + return false; + } +#endif + + bool UpdatePlaylistFile(Directory &directory, + const char *name, const char *suffix, + const struct stat *st); + + bool UpdateRegularFile(Directory &directory, + const char *name, const struct stat *st); + + void UpdateDirectoryChild(Directory &directory, + const char *name, const struct stat *st); + + bool UpdateDirectory(Directory &directory, const struct stat *st); + + /** + * Create the specified directory object if it does not exist + * already or if the #stat object indicates that it has been + * modified since the last update. Returns nullptr when it + * exists already and is unmodified. + * + * The caller must lock the database. + */ + Directory *MakeDirectoryIfModified(Directory &parent, const char *name, + const struct stat *st); + + Directory *DirectoryMakeChildChecked(Directory &parent, + const char *name_utf8); + + Directory *DirectoryMakeUriParentChecked(const char *uri); + + void UpdateUri(const char *uri); +}; + +#endif