diff options
Diffstat (limited to 'src/db/update')
-rw-r--r-- | src/db/update/Container.cxx | 3 | ||||
-rw-r--r-- | src/db/update/Queue.cxx | 29 | ||||
-rw-r--r-- | src/db/update/Queue.hxx | 25 | ||||
-rw-r--r-- | src/db/update/Service.cxx | 101 | ||||
-rw-r--r-- | src/db/update/Service.hxx | 13 | ||||
-rw-r--r-- | src/db/update/Walk.cxx | 6 |
6 files changed, 160 insertions, 17 deletions
diff --git a/src/db/update/Container.cxx b/src/db/update/Container.cxx index 9f8d84839..1c420fa99 100644 --- a/src/db/update/Container.cxx +++ b/src/db/update/Container.cxx @@ -42,6 +42,9 @@ UpdateWalk::MakeDirectoryIfModified(Directory &parent, const char *name, // directory exists already if (directory != nullptr) { + if (directory->IsMount()) + return nullptr; + if (directory->mtime == info.mtime && !walk_discard) { /* not modified */ return nullptr; diff --git a/src/db/update/Queue.cxx b/src/db/update/Queue.cxx index 096a39a8c..6d6d80131 100644 --- a/src/db/update/Queue.cxx +++ b/src/db/update/Queue.cxx @@ -21,12 +21,13 @@ #include "Queue.hxx" bool -UpdateQueue::Push(const char *path, bool discard, unsigned id) +UpdateQueue::Push(SimpleDatabase &db, Storage &storage, + const char *path, bool discard, unsigned id) { if (update_queue.size() >= MAX_UPDATE_QUEUE_SIZE) return false; - update_queue.emplace_back(path, discard, id); + update_queue.emplace_back(db, storage, path, discard, id); return true; } @@ -40,3 +41,27 @@ UpdateQueue::Pop() update_queue.pop_front(); return i; } + +void +UpdateQueue::Erase(SimpleDatabase &db) +{ + for (auto i = update_queue.begin(), end = update_queue.end(); + i != end;) { + if (i->db == &db) + i = update_queue.erase(i); + else + ++i; + } +} + +void +UpdateQueue::Erase(Storage &storage) +{ + for (auto i = update_queue.begin(), end = update_queue.end(); + i != end;) { + if (i->storage == &storage) + i = update_queue.erase(i); + else + ++i; + } +} diff --git a/src/db/update/Queue.hxx b/src/db/update/Queue.hxx index 039c62fe3..9064ea481 100644 --- a/src/db/update/Queue.hxx +++ b/src/db/update/Queue.hxx @@ -21,19 +21,30 @@ #define MPD_UPDATE_QUEUE_HXX #include "check.h" +#include "Compiler.h" #include <string> #include <list> +class SimpleDatabase; +class Storage; + struct UpdateQueueItem { + SimpleDatabase *db; + Storage *storage; + std::string path_utf8; unsigned id; bool discard; UpdateQueueItem():id(0) {} - UpdateQueueItem(const char *_path, bool _discard, + + UpdateQueueItem(SimpleDatabase &_db, + Storage &_storage, + const char *_path, bool _discard, unsigned _id) - :path_utf8(_path), id(_id), discard(_discard) {} + :db(&_db), storage(&_storage), path_utf8(_path), + id(_id), discard(_discard) {} bool IsDefined() const { return id != 0; @@ -46,13 +57,21 @@ class UpdateQueue { std::list<UpdateQueueItem> update_queue; public: - bool Push(const char *path, bool discard, unsigned id); + gcc_nonnull_all + bool Push(SimpleDatabase &db, Storage &storage, + const char *path, bool discard, unsigned id); UpdateQueueItem Pop(); void Clear() { update_queue.clear(); } + + gcc_nonnull_all + void Erase(SimpleDatabase &db); + + gcc_nonnull_all + void Erase(Storage &storage); }; #endif diff --git a/src/db/update/Service.cxx b/src/db/update/Service.cxx index 2971998e4..e8a1f6b02 100644 --- a/src/db/update/Service.cxx +++ b/src/db/update/Service.cxx @@ -22,7 +22,10 @@ #include "Walk.hxx" #include "UpdateDomain.hxx" #include "db/DatabaseListener.hxx" +#include "db/DatabaseLock.hxx" #include "db/plugins/simple/SimpleDatabasePlugin.hxx" +#include "db/plugins/simple/Directory.hxx" +#include "storage/CompositeStorage.hxx" #include "Idle.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -39,7 +42,7 @@ #include <assert.h> UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db, - Storage &_storage, + CompositeStorage &_storage, DatabaseListener &_listener) :DeferredMonitor(_loop), db(_db), storage(_storage), @@ -71,6 +74,42 @@ UpdateService::CancelAllAsync() walk->Cancel(); } +void +UpdateService::CancelMount(const char *uri) +{ + /* determine which (mounted) database will be updated and what + storage will be scanned */ + + db_lock(); + const auto lr = db.GetRoot().LookupDirectory(uri); + db_unlock(); + + if (!lr.directory->IsMount()) + return; + + bool cancel_current = false; + + Storage *storage2 = storage.GetMount(uri); + if (storage2 != nullptr) { + queue.Erase(*storage2); + cancel_current = next.IsDefined() && next.storage == storage2; + } + + Database &_db2 = *lr.directory->mounted_database; + if (_db2.IsPlugin(simple_db_plugin)) { + SimpleDatabase &db2 = static_cast<SimpleDatabase &>(_db2); + queue.Erase(db2); + cancel_current |= next.IsDefined() && next.db == &db2; + } + + if (cancel_current && walk != nullptr) { + walk->Cancel(); + + if (update_thread.IsDefined()) + update_thread.Join(); + } +} + inline void UpdateService::Task() { @@ -84,12 +123,12 @@ UpdateService::Task() SetThreadIdlePriority(); - modified = walk->Walk(db.GetRoot(), next.path_utf8.c_str(), + modified = walk->Walk(next.db->GetRoot(), next.path_utf8.c_str(), next.discard); - if (modified || !db.FileExists()) { + if (modified || !next.db->FileExists()) { Error error; - if (!db.Save(error)) + if (!next.db->Save(error)) LogError(error, "Failed to save database"); } @@ -120,7 +159,7 @@ UpdateService::StartThread(UpdateQueueItem &&i) modified = false; next = std::move(i); - walk = new UpdateWalk(GetEventLoop(), listener, storage); + walk = new UpdateWalk(GetEventLoop(), listener, *next.storage); Error error; if (!update_thread.Start(Task, this, error)) @@ -144,9 +183,52 @@ UpdateService::Enqueue(const char *path, bool discard) { assert(GetEventLoop().IsInsideOrNull()); + /* determine which (mounted) database will be updated and what + storage will be scanned */ + SimpleDatabase *db2; + Storage *storage2; + + db_lock(); + const auto lr = db.GetRoot().LookupDirectory(path); + db_unlock(); + if (lr.directory->IsMount()) { + /* follow the mountpoint, update the mounted + database */ + + Database &_db2 = *lr.directory->mounted_database; + if (!_db2.IsPlugin(simple_db_plugin)) + /* cannot update this type of database */ + return 0; + + db2 = static_cast<SimpleDatabase *>(&_db2); + + if (lr.uri == nullptr) { + storage2 = storage.GetMount(path); + path = ""; + } else { + assert(lr.uri > path); + assert(lr.uri < path + strlen(path)); + assert(lr.uri[-1] == '/'); + + const std::string mountpoint(path, lr.uri - 1); + storage2 = storage.GetMount(mountpoint.c_str()); + path = lr.uri; + } + } else { + /* use the "root" database/storage */ + + db2 = &db; + storage2 = storage.GetMount(""); + } + + if (storage2 == nullptr) + /* no storage found at this mount point - should not + happen */ + return 0; + if (progress != UPDATE_PROGRESS_IDLE) { const unsigned id = GenerateId(); - if (!queue.Push(path, discard, id)) + if (!queue.Push(*db2, *storage2, path, discard, id)) return 0; update_task_id = id; @@ -154,7 +236,7 @@ UpdateService::Enqueue(const char *path, bool discard) } const unsigned id = update_task_id = GenerateId(); - StartThread(UpdateQueueItem(path, discard, id)); + StartThread(UpdateQueueItem(*db2, *storage2, path, discard, id)); idle_add(IDLE_UPDATE); @@ -171,7 +253,10 @@ UpdateService::RunDeferred() assert(next.IsDefined()); assert(walk != nullptr); - update_thread.Join(); + /* wait for thread to finish only if it wasn't cancelled by + CancelMount() */ + if (update_thread.IsDefined()) + update_thread.Join(); delete walk; walk = nullptr; diff --git a/src/db/update/Service.hxx b/src/db/update/Service.hxx index e42a0fa5a..cbb4a3f9d 100644 --- a/src/db/update/Service.hxx +++ b/src/db/update/Service.hxx @@ -28,7 +28,7 @@ class SimpleDatabase; class DatabaseListener; class UpdateWalk; -class Storage; +class CompositeStorage; /** * This class manages the update queue and runs the update thread. @@ -41,7 +41,7 @@ class UpdateService final : DeferredMonitor { }; SimpleDatabase &db; - Storage &storage; + CompositeStorage &storage; DatabaseListener &listener; @@ -63,7 +63,7 @@ class UpdateService final : DeferredMonitor { public: UpdateService(EventLoop &_loop, SimpleDatabase &_db, - Storage &_storage, + CompositeStorage &_storage, DatabaseListener &_listener); ~UpdateService(); @@ -92,6 +92,13 @@ public: */ void CancelAllAsync(); + /** + * Cancel all updates for the given mount point. If an update + * is already running for it, the method will wait for + * cancellation to complete. + */ + void CancelMount(const char *uri); + private: /* virtual methods from class DeferredMonitor */ virtual void RunDeferred() override; diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 201030f25..c329865ff 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -397,8 +397,12 @@ UpdateWalk::DirectoryMakeChildChecked(Directory &parent, Directory *directory = parent.FindChild(name_utf8); db_unlock(); - if (directory != nullptr) + if (directory != nullptr) { + if (directory->IsMount()) + directory = nullptr; + return directory; + } FileInfo info; if (!GetInfo(storage, uri_utf8, info) || |