From 681e012fb542ee1bb2ea5312dc673987a7a8ee29 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 27 Feb 2014 16:36:11 +0100 Subject: [PATCH] db/update: cancel the update on shutdown --- NEWS | 1 + src/Main.cxx | 3 +++ src/db/update/Queue.hxx | 4 ++++ src/db/update/Service.cxx | 19 +++++++++++++++++++ src/db/update/Service.hxx | 8 ++++++++ src/db/update/Walk.cxx | 2 +- src/db/update/Walk.hxx | 23 +++++++++++++++++++++++ 7 files changed, 59 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 3883a51e3..afc5e7c63 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ ver 0.19 (not yet released) - proxy: forward "idle" events - proxy: copy "Last-Modified" from remote directories - upnp: new plugin + - cancel the update on shutdown * storage - music_directory can point to a remote file server - nfs: new plugin diff --git a/src/Main.cxx b/src/Main.cxx index 4b161fd6b..61e5fb2b6 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -596,6 +596,9 @@ int mpd_main(int argc, char *argv[]) #if defined(ENABLE_DATABASE) && defined(ENABLE_INOTIFY) mpd_inotify_finish(); + + if (instance->update != nullptr) + instance->update->CancelAllAsync(); #endif if (state_file != nullptr) { diff --git a/src/db/update/Queue.hxx b/src/db/update/Queue.hxx index a5b2aa9ac..eb0b268bf 100644 --- a/src/db/update/Queue.hxx +++ b/src/db/update/Queue.hxx @@ -50,6 +50,10 @@ public: bool Push(const char *path, bool discard, unsigned id); UpdateQueueItem Pop(); + + void Clear() { + update_queue = decltype(update_queue)(); + } }; #endif diff --git a/src/db/update/Service.cxx b/src/db/update/Service.cxx index 0018e2cc0..112d97760 100644 --- a/src/db/update/Service.cxx +++ b/src/db/update/Service.cxx @@ -47,6 +47,23 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db, { } +UpdateService::~UpdateService() +{ + CancelAllAsync(); + + if (update_thread.IsDefined()) + update_thread.Join(); +} + +void +UpdateService::CancelAllAsync() +{ + assert(GetEventLoop().IsInsideOrNull()); + + queue.Clear(); + walk.Cancel(); +} + inline void UpdateService::Task() { @@ -94,6 +111,8 @@ UpdateService::StartThread(UpdateQueueItem &&i) next = std::move(i); + walk.Prepare(); + Error error; if (!update_thread.Start(Task, this, error)) FatalError(error); diff --git a/src/db/update/Service.hxx b/src/db/update/Service.hxx index 936c2bb55..ebdda7bd1 100644 --- a/src/db/update/Service.hxx +++ b/src/db/update/Service.hxx @@ -64,6 +64,8 @@ public: Storage &_storage, DatabaseListener &_listener); + ~UpdateService(); + /** * Returns a non-zero job id when we are currently updating * the database. @@ -82,6 +84,12 @@ public: gcc_nonnull_all unsigned Enqueue(const char *path, bool discard); + /** + * Clear the queue and cancel the current update. Does not + * wait for the thread to exit. + */ + void CancelAllAsync(); + 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 c65480873..db05b1823 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -358,7 +358,7 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info) PurgeDeletedFromDirectory(directory); const char *name_utf8; - while ((name_utf8 = reader->Read()) != nullptr) { + while (!cancel && (name_utf8 = reader->Read()) != nullptr) { if (skip_path(name_utf8)) continue; diff --git a/src/db/update/Walk.hxx b/src/db/update/Walk.hxx index bf3eb8ae4..353f6f3ed 100644 --- a/src/db/update/Walk.hxx +++ b/src/db/update/Walk.hxx @@ -48,6 +48,13 @@ class UpdateWalk final { bool walk_discard; bool modified; + /** + * Set to true by the main thread when the update thread shall + * cancel as quickly as possible. Access to this flag is + * unprotected. + */ + volatile bool cancel; + Storage &storage; DatabaseEditor editor; @@ -56,6 +63,22 @@ public: UpdateWalk(EventLoop &_loop, DatabaseListener &_listener, Storage &_storage); + /** + * Cancel the current update and quit the Walk() method as + * soon as possible. + */ + void Cancel() { + cancel = true; + } + + /** + * Call from the main thread before starting the update + * thread. + */ + void Prepare() { + cancel = false; + } + /** * Returns true if the database was modified. */