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 "Remove.hxx"
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "db/plugins/simple/Song.hxx"
|
#include "db/plugins/simple/Song.hxx"
|
||||||
#include "db/LightSong.hxx"
|
|
||||||
#include "db/DatabaseListener.hxx"
|
#include "db/DatabaseListener.hxx"
|
||||||
#include "Log.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.
|
* main task, to be sure that there is no pointer left to it.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
UpdateRemoveService::RunDeferred()
|
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());
|
FormatDefault(update_domain, "removing %s", uri.c_str());
|
||||||
listener.OnDatabaseSongRemoved(uri.c_str());
|
listener.OnDatabaseSongRemoved(uri.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear "removed_song" and send signal to update thread */
|
/* note: if Remove() was called in the meantime, it saw an
|
||||||
remove_mutex.lock();
|
empty list, and scheduled another event */
|
||||||
removed_song = nullptr;
|
|
||||||
remove_cond.signal();
|
|
||||||
remove_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UpdateRemoveService::Remove(const Song *song)
|
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();
|
/* inject an event into the main thread, but only if the list
|
||||||
|
was empty; if it was not, then that even was already
|
||||||
remove_mutex.lock();
|
pending */
|
||||||
|
if (was_empty)
|
||||||
while (removed_song != nullptr)
|
DeferredMonitor::Schedule();
|
||||||
remove_cond.wait(remove_mutex);
|
|
||||||
|
|
||||||
remove_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,11 @@
|
|||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "event/DeferredMonitor.hxx"
|
#include "event/DeferredMonitor.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "thread/Cond.hxx"
|
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
|
#include <forward_list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct Song;
|
struct Song;
|
||||||
class DatabaseListener;
|
class DatabaseListener;
|
||||||
|
|
||||||
@ -36,15 +38,13 @@ class DatabaseListener;
|
|||||||
class UpdateRemoveService final : DeferredMonitor {
|
class UpdateRemoveService final : DeferredMonitor {
|
||||||
DatabaseListener &listener;
|
DatabaseListener &listener;
|
||||||
|
|
||||||
Mutex remove_mutex;
|
Mutex mutex;
|
||||||
Cond remove_cond;
|
|
||||||
|
|
||||||
const Song *removed_song;
|
std::forward_list<std::string> uris;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UpdateRemoveService(EventLoop &_loop, DatabaseListener &_listener)
|
UpdateRemoveService(EventLoop &_loop, DatabaseListener &_listener)
|
||||||
:DeferredMonitor(_loop), listener(_listener),
|
:DeferredMonitor(_loop), listener(_listener) {}
|
||||||
removed_song(nullptr){}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a signal to the main thread which will in turn remove
|
* Sends a signal to the main thread which will in turn remove
|
||||||
|
Loading…
Reference in New Issue
Block a user