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:
parent
2edad38c7c
commit
42f7df9681
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue