db/update/Remove: reimplement as a non-blocking queue

This reduces overhead for two reasons:

1. calls to Remove() are non-blocking

2. RunDeferred() may work on large chunks at a time, reducing the
   number of RunDeferred() calls
This commit is contained in:
Max Kellermann 2016-03-18 16:43:02 +01:00
parent 2edad38c7c
commit 42f7df9681
2 changed files with 29 additions and 27 deletions

View File

@ -21,47 +21,49 @@
#include "Remove.hxx"
#include "UpdateDomain.hxx"
#include "db/plugins/simple/Song.hxx"
#include "db/LightSong.hxx"
#include "db/DatabaseListener.hxx"
#include "Log.hxx"
#include <assert.h>
/**
* Safely remove a song from the database. This must be done in the
* Safely remove songs from the database. This must be done in the
* main task, to be sure that there is no pointer left to it.
*/
void
UpdateRemoveService::RunDeferred()
{
assert(removed_song != nullptr);
/* copy the list and unlock the mutex before invoking
callbacks */
std::forward_list<std::string> copy;
{
const auto uri = removed_song->GetURI();
const ScopeLock protect(mutex);
std::swap(uris, copy);
}
for (const auto &uri : copy) {
FormatDefault(update_domain, "removing %s", uri.c_str());
listener.OnDatabaseSongRemoved(uri.c_str());
}
/* clear "removed_song" and send signal to update thread */
remove_mutex.lock();
removed_song = nullptr;
remove_cond.signal();
remove_mutex.unlock();
/* note: if Remove() was called in the meantime, it saw an
empty list, and scheduled another event */
}
void
UpdateRemoveService::Remove(const Song *song)
{
assert(removed_song == nullptr);
bool was_empty;
removed_song = song;
{
const ScopeLock protect(mutex);
was_empty = uris.empty();
uris.emplace_front(song->GetURI());
}
DeferredMonitor::Schedule();
remove_mutex.lock();
while (removed_song != nullptr)
remove_cond.wait(remove_mutex);
remove_mutex.unlock();
/* inject an event into the main thread, but only if the list
was empty; if it was not, then that even was already
pending */
if (was_empty)
DeferredMonitor::Schedule();
}

View File

@ -23,9 +23,11 @@
#include "check.h"
#include "event/DeferredMonitor.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "Compiler.h"
#include <forward_list>
#include <string>
struct Song;
class DatabaseListener;
@ -36,15 +38,13 @@ class DatabaseListener;
class UpdateRemoveService final : DeferredMonitor {
DatabaseListener &listener;
Mutex remove_mutex;
Cond remove_cond;
Mutex mutex;
const Song *removed_song;
std::forward_list<std::string> uris;
public:
UpdateRemoveService(EventLoop &_loop, DatabaseListener &_listener)
:DeferredMonitor(_loop), listener(_listener),
removed_song(nullptr){}
:DeferredMonitor(_loop), listener(_listener) {}
/**
* Sends a signal to the main thread which will in turn remove