db/update: convert to OO API
Move global variables into the new classes. That may allow multiple update threads for multiple databases one day.
This commit is contained in:
parent
a31738f6f1
commit
c6725884bc
18
Makefile.am
18
Makefile.am
|
@ -124,15 +124,15 @@ src_mpd_SOURCES = \
|
|||
src/filter/FilterInternal.hxx \
|
||||
src/filter/FilterRegistry.cxx src/filter/FilterRegistry.hxx \
|
||||
src/db/update/UpdateDomain.cxx src/db/update/UpdateDomain.hxx \
|
||||
src/db/update/UpdateGlue.cxx src/db/update/UpdateGlue.hxx \
|
||||
src/db/update/UpdateQueue.cxx src/db/update/UpdateQueue.hxx \
|
||||
src/db/update/Service.cxx src/db/update/Service.hxx \
|
||||
src/db/update/UpdateGlue.cxx \
|
||||
src/db/update/Queue.cxx src/db/update/Queue.hxx \
|
||||
src/db/update/UpdateIO.cxx src/db/update/UpdateIO.hxx \
|
||||
src/db/update/UpdateDatabase.cxx src/db/update/UpdateDatabase.hxx \
|
||||
src/db/update/UpdateWalk.cxx src/db/update/UpdateWalk.hxx \
|
||||
src/db/update/UpdateSong.cxx src/db/update/UpdateSong.hxx \
|
||||
src/db/update/UpdateContainer.cxx src/db/update/UpdateContainer.hxx \
|
||||
src/db/update/UpdateInternal.hxx \
|
||||
src/db/update/UpdateRemove.cxx src/db/update/UpdateRemove.hxx \
|
||||
src/db/update/Editor.cxx src/db/update/Editor.hxx \
|
||||
src/db/update/Walk.cxx src/db/update/Walk.hxx \
|
||||
src/db/update/UpdateSong.cxx \
|
||||
src/db/update/Container.cxx \
|
||||
src/db/update/Remove.cxx src/db/update/Remove.hxx \
|
||||
src/db/update/ExcludeList.cxx src/db/update/ExcludeList.hxx \
|
||||
src/client/Client.cxx src/client/Client.hxx \
|
||||
src/client/ClientInternal.hxx \
|
||||
|
@ -486,7 +486,7 @@ if ENABLE_ARCHIVE
|
|||
noinst_LIBRARIES += libarchive.a
|
||||
|
||||
src_mpd_SOURCES += \
|
||||
src/db/update/UpdateArchive.cxx src/db/update/UpdateArchive.hxx
|
||||
src/db/update/Archive.cxx
|
||||
|
||||
libarchive_a_SOURCES = \
|
||||
src/archive/ArchiveDomain.cxx src/archive/ArchiveDomain.hxx \
|
||||
|
|
|
@ -33,9 +33,6 @@ class EventLoop;
|
|||
|
||||
namespace GlobalEvents {
|
||||
enum Event {
|
||||
/** database update was finished */
|
||||
UPDATE,
|
||||
|
||||
/** during database update, a song was deleted */
|
||||
DELETE,
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
class NeighborGlue;
|
||||
#endif
|
||||
|
||||
class UpdateService;
|
||||
class ClientList;
|
||||
struct Partition;
|
||||
|
||||
|
@ -42,6 +43,8 @@ struct Instance final
|
|||
NeighborGlue *neighbors;
|
||||
#endif
|
||||
|
||||
UpdateService *update;
|
||||
|
||||
ClientList *client_list;
|
||||
|
||||
Partition *partition;
|
||||
|
|
17
src/Main.cxx
17
src/Main.cxx
|
@ -23,7 +23,7 @@
|
|||
#include "CommandLine.hxx"
|
||||
#include "PlaylistFile.hxx"
|
||||
#include "PlaylistGlobal.hxx"
|
||||
#include "db/update/UpdateGlue.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "StateFile.hxx"
|
||||
#include "PlayerThread.hxx"
|
||||
|
@ -448,10 +448,13 @@ int mpd_main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
decoder_plugin_init_all();
|
||||
update_global_init();
|
||||
|
||||
const bool create_db = !glue_db_init_and_load();
|
||||
|
||||
instance->update = db_is_simple()
|
||||
? new UpdateService(*main_loop)
|
||||
: nullptr;
|
||||
|
||||
glue_sticker_init();
|
||||
|
||||
command_init();
|
||||
|
@ -490,7 +493,7 @@ int mpd_main(int argc, char *argv[])
|
|||
if (create_db) {
|
||||
/* the database failed to load: recreate the
|
||||
database */
|
||||
unsigned job = update_enqueue("", true);
|
||||
unsigned job = instance->update->Enqueue("", true);
|
||||
if (job == 0)
|
||||
FatalError("directory update failed");
|
||||
}
|
||||
|
@ -504,8 +507,9 @@ int mpd_main(int argc, char *argv[])
|
|||
|
||||
if (config_get_bool(CONF_AUTO_UPDATE, false)) {
|
||||
#ifdef ENABLE_INOTIFY
|
||||
if (mapper_has_music_directory())
|
||||
mpd_inotify_init(*main_loop,
|
||||
if (mapper_has_music_directory() &&
|
||||
instance->update != nullptr)
|
||||
mpd_inotify_init(*main_loop, *instance->update,
|
||||
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||
G_MAXUINT));
|
||||
#else
|
||||
|
@ -558,6 +562,8 @@ int mpd_main(int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
delete instance->update;
|
||||
|
||||
const clock_t start = clock();
|
||||
DatabaseGlobalDeinit();
|
||||
FormatDebug(main_domain,
|
||||
|
@ -575,7 +581,6 @@ int mpd_main(int argc, char *argv[])
|
|||
mapper_finish();
|
||||
delete instance->partition;
|
||||
command_finish();
|
||||
update_global_finish();
|
||||
decoder_plugin_deinit_all();
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_deinit_all();
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "config.h"
|
||||
#include "OtherCommands.hxx"
|
||||
#include "DatabaseCommands.hxx"
|
||||
#include "db/update/UpdateGlue.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "CommandError.hxx"
|
||||
#include "db/Uri.hxx"
|
||||
#include "DetachedSong.hxx"
|
||||
|
@ -46,6 +46,7 @@
|
|||
#include "client/ClientFile.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "Idle.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -186,7 +187,6 @@ CommandResult
|
|||
handle_update(Client &client, gcc_unused int argc, char *argv[])
|
||||
{
|
||||
const char *path = "";
|
||||
unsigned ret;
|
||||
|
||||
assert(argc <= 2);
|
||||
if (argc == 2) {
|
||||
|
@ -202,7 +202,13 @@ handle_update(Client &client, gcc_unused int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
ret = update_enqueue(path, false);
|
||||
UpdateService *update = client.partition.instance.update;
|
||||
if (update == nullptr) {
|
||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
unsigned ret = update->Enqueue(path, false);
|
||||
if (ret > 0) {
|
||||
client_printf(client, "updating_db: %i\n", ret);
|
||||
return CommandResult::OK;
|
||||
|
@ -217,7 +223,6 @@ CommandResult
|
|||
handle_rescan(Client &client, gcc_unused int argc, char *argv[])
|
||||
{
|
||||
const char *path = "";
|
||||
unsigned ret;
|
||||
|
||||
assert(argc <= 2);
|
||||
if (argc == 2) {
|
||||
|
@ -230,7 +235,13 @@ handle_rescan(Client &client, gcc_unused int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
ret = update_enqueue(path, true);
|
||||
UpdateService *update = client.partition.instance.update;
|
||||
if (update == nullptr) {
|
||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
unsigned ret = update->Enqueue(path, true);
|
||||
if (ret > 0) {
|
||||
client_printf(client, "updating_db: %i\n", ret);
|
||||
return CommandResult::OK;
|
||||
|
|
|
@ -22,10 +22,11 @@
|
|||
#include "CommandError.hxx"
|
||||
#include "Playlist.hxx"
|
||||
#include "PlaylistPrint.hxx"
|
||||
#include "db/update/UpdateGlue.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "protocol/Result.hxx"
|
||||
#include "protocol/ArgParser.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
|
@ -111,7 +112,6 @@ handle_status(Client &client,
|
|||
gcc_unused int argc, gcc_unused char *argv[])
|
||||
{
|
||||
const char *state = nullptr;
|
||||
int updateJobId;
|
||||
int song;
|
||||
|
||||
const auto player_status = client.player_control.GetStatus();
|
||||
|
@ -187,7 +187,11 @@ handle_status(Client &client,
|
|||
}
|
||||
}
|
||||
|
||||
if ((updateJobId = isUpdatingDB())) {
|
||||
const UpdateService *update_service = client.partition.instance.update;
|
||||
unsigned updateJobId = update_service != nullptr
|
||||
? update_service->GetId()
|
||||
: 0;
|
||||
if (updateJobId != 0) {
|
||||
client_printf(client,
|
||||
COMMAND_STATUS_UPDATING_DB ": %i\n",
|
||||
updateJobId);
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateArchive.hxx"
|
||||
#include "UpdateInternal.hxx"
|
||||
#include "Walk.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/Directory.hxx"
|
||||
|
@ -35,10 +34,11 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
update_archive_tree(Directory &directory, const char *name)
|
||||
void
|
||||
UpdateWalk::UpdateArchiveTree(Directory &directory, const char *name)
|
||||
{
|
||||
const char *tmp = strchr(name, '/');
|
||||
if (tmp) {
|
||||
|
@ -51,7 +51,7 @@ update_archive_tree(Directory &directory, const char *name)
|
|||
db_unlock();
|
||||
|
||||
//create directories first
|
||||
update_archive_tree(*subdir, tmp+1);
|
||||
UpdateArchiveTree(*subdir, tmp + 1);
|
||||
} else {
|
||||
if (strlen(name) == 0) {
|
||||
LogWarning(update_domain,
|
||||
|
@ -78,6 +78,21 @@ update_archive_tree(Directory &directory, const char *name)
|
|||
}
|
||||
}
|
||||
|
||||
class UpdateArchiveVisitor final : public ArchiveVisitor {
|
||||
UpdateWalk &walk;
|
||||
Directory *directory;
|
||||
|
||||
public:
|
||||
UpdateArchiveVisitor(UpdateWalk &_walk, Directory *_directory)
|
||||
:walk(_walk), directory(_directory) {}
|
||||
|
||||
virtual void VisitArchiveEntry(const char *path_utf8) override {
|
||||
FormatDebug(update_domain,
|
||||
"adding archive file: %s", path_utf8);
|
||||
walk.UpdateArchiveTree(*directory, path_utf8);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the file listing from an archive file.
|
||||
*
|
||||
|
@ -86,10 +101,10 @@ update_archive_tree(Directory &directory, const char *name)
|
|||
* @param st stat() information on the archive file
|
||||
* @param plugin the archive plugin which fits this archive type
|
||||
*/
|
||||
static void
|
||||
update_archive_file2(Directory &parent, const char *name,
|
||||
void
|
||||
UpdateWalk::UpdateArchiveFile(Directory &parent, const char *name,
|
||||
const struct stat *st,
|
||||
const struct archive_plugin *plugin)
|
||||
const archive_plugin &plugin)
|
||||
{
|
||||
db_lock();
|
||||
Directory *directory = parent.FindChild(name);
|
||||
|
@ -105,7 +120,7 @@ update_archive_file2(Directory &parent, const char *name,
|
|||
|
||||
/* open archive */
|
||||
Error error;
|
||||
ArchiveFile *file = archive_file_open(plugin, path_fs.c_str(), error);
|
||||
ArchiveFile *file = archive_file_open(&plugin, path_fs.c_str(), error);
|
||||
if (file == nullptr) {
|
||||
LogError(error);
|
||||
return;
|
||||
|
@ -126,44 +141,21 @@ update_archive_file2(Directory &parent, const char *name,
|
|||
|
||||
directory->mtime = st->st_mtime;
|
||||
|
||||
class UpdateArchiveVisitor final : public ArchiveVisitor {
|
||||
Directory *directory;
|
||||
|
||||
public:
|
||||
UpdateArchiveVisitor(Directory *_directory)
|
||||
:directory(_directory) {}
|
||||
|
||||
virtual void VisitArchiveEntry(const char *path_utf8) override {
|
||||
FormatDebug(update_domain,
|
||||
"adding archive file: %s", path_utf8);
|
||||
update_archive_tree(*directory, path_utf8);
|
||||
}
|
||||
};
|
||||
|
||||
UpdateArchiveVisitor visitor(directory);
|
||||
UpdateArchiveVisitor visitor(*this, directory);
|
||||
file->Visit(visitor);
|
||||
file->Close();
|
||||
}
|
||||
|
||||
bool
|
||||
update_archive_file(Directory &directory,
|
||||
UpdateWalk::UpdateArchiveFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st)
|
||||
{
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
const struct archive_plugin *plugin =
|
||||
archive_plugin_from_suffix(suffix);
|
||||
if (plugin == nullptr)
|
||||
return false;
|
||||
|
||||
update_archive_file2(directory, name, st, plugin);
|
||||
UpdateArchiveFile(directory, name, st, *plugin);
|
||||
return true;
|
||||
#else
|
||||
(void)directory;
|
||||
(void)name;
|
||||
(void)suffix;
|
||||
(void)st;
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
|
@ -18,9 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateContainer.hxx"
|
||||
#include "UpdateInternal.hxx"
|
||||
#include "UpdateDatabase.hxx"
|
||||
#include "Walk.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/Directory.hxx"
|
||||
|
@ -33,18 +31,12 @@
|
|||
#include "tag/TagBuilder.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* Create the specified directory object if it does not exist already
|
||||
* or if the #stat object indicates that it has been modified since
|
||||
* the last update. Returns nullptr when it exists already and is
|
||||
* unmodified.
|
||||
*
|
||||
* The caller must lock the database.
|
||||
*/
|
||||
static Directory *
|
||||
make_directory_if_modified(Directory &parent, const char *name,
|
||||
Directory *
|
||||
UpdateWalk::MakeDirectoryIfModified(Directory &parent, const char *name,
|
||||
const struct stat *st)
|
||||
{
|
||||
Directory *directory = parent.FindChild(name);
|
||||
|
@ -56,7 +48,7 @@ make_directory_if_modified(Directory &parent, const char *name,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
delete_directory(directory);
|
||||
editor.DeleteDirectory(directory);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
@ -73,10 +65,9 @@ SupportsContainerSuffix(const DecoderPlugin &plugin, const char *suffix)
|
|||
}
|
||||
|
||||
bool
|
||||
update_container_file(Directory &directory,
|
||||
const char *name,
|
||||
const struct stat *st,
|
||||
const char *suffix)
|
||||
UpdateWalk::UpdateContainerFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st)
|
||||
{
|
||||
const DecoderPlugin *_plugin = decoder_plugins_find([suffix](const DecoderPlugin &plugin){
|
||||
return SupportsContainerSuffix(plugin, suffix);
|
||||
|
@ -86,7 +77,7 @@ update_container_file(Directory &directory,
|
|||
const DecoderPlugin &plugin = *_plugin;
|
||||
|
||||
db_lock();
|
||||
Directory *contdir = make_directory_if_modified(directory, name, st);
|
||||
Directory *contdir = MakeDirectoryIfModified(directory, name, st);
|
||||
if (contdir == nullptr) {
|
||||
/* not modified */
|
||||
db_unlock();
|
||||
|
@ -128,7 +119,7 @@ update_container_file(Directory &directory,
|
|||
|
||||
if (tnum == 1) {
|
||||
db_lock();
|
||||
delete_directory(contdir);
|
||||
editor.DeleteDirectory(contdir);
|
||||
db_unlock();
|
||||
return false;
|
||||
} else
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateDatabase.hxx"
|
||||
#include "UpdateRemove.hxx"
|
||||
#include "Editor.hxx"
|
||||
#include "Remove.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "db/Directory.hxx"
|
||||
#include "db/Song.hxx"
|
||||
|
@ -29,7 +29,7 @@
|
|||
#include <stddef.h>
|
||||
|
||||
void
|
||||
delete_song(Directory &dir, Song *del)
|
||||
DatabaseEditor::DeleteSong(Directory &dir, Song *del)
|
||||
{
|
||||
assert(del->parent == &dir);
|
||||
|
||||
|
@ -39,7 +39,7 @@ delete_song(Directory &dir, Song *del)
|
|||
db_unlock(); /* temporary unlock, because update_remove_song() blocks */
|
||||
|
||||
/* now take it out of the playlist (in the main_task) */
|
||||
update_remove_song(del);
|
||||
remove.Remove(del);
|
||||
|
||||
/* finally, all possible references gone, free it */
|
||||
del->Free();
|
||||
|
@ -53,32 +53,32 @@ delete_song(Directory &dir, Song *del)
|
|||
*
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
static void
|
||||
clear_directory(Directory &directory)
|
||||
inline void
|
||||
DatabaseEditor::ClearDirectory(Directory &directory)
|
||||
{
|
||||
Directory *child, *n;
|
||||
directory_for_each_child_safe(child, n, directory)
|
||||
delete_directory(child);
|
||||
DeleteDirectory(child);
|
||||
|
||||
Song *song, *ns;
|
||||
directory_for_each_song_safe(song, ns, directory) {
|
||||
assert(song->parent == &directory);
|
||||
delete_song(directory, song);
|
||||
DeleteSong(directory, song);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delete_directory(Directory *directory)
|
||||
DatabaseEditor::DeleteDirectory(Directory *directory)
|
||||
{
|
||||
assert(directory->parent != nullptr);
|
||||
|
||||
clear_directory(*directory);
|
||||
ClearDirectory(*directory);
|
||||
|
||||
directory->Delete();
|
||||
}
|
||||
|
||||
bool
|
||||
delete_name_in(Directory &parent, const char *name)
|
||||
DatabaseEditor::DeleteNameIn(Directory &parent, const char *name)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
|
@ -86,13 +86,13 @@ delete_name_in(Directory &parent, const char *name)
|
|||
Directory *directory = parent.FindChild(name);
|
||||
|
||||
if (directory != nullptr) {
|
||||
delete_directory(directory);
|
||||
DeleteDirectory(directory);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
Song *song = parent.FindSong(name);
|
||||
if (song != nullptr) {
|
||||
delete_song(parent, song);
|
||||
DeleteSong(parent, song);
|
||||
modified = true;
|
||||
}
|
||||
|
|
@ -21,30 +21,40 @@
|
|||
#define MPD_UPDATE_DATABASE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Remove.hxx"
|
||||
|
||||
struct Directory;
|
||||
struct Song;
|
||||
class UpdateRemoveService;
|
||||
|
||||
class DatabaseEditor final {
|
||||
UpdateRemoveService remove;
|
||||
|
||||
public:
|
||||
DatabaseEditor(EventLoop &_loop)
|
||||
:remove(_loop) {}
|
||||
|
||||
/**
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
void
|
||||
delete_song(Directory &parent, Song *song);
|
||||
void DeleteSong(Directory &parent, Song *song);
|
||||
|
||||
/**
|
||||
* Recursively free a directory and all its contents.
|
||||
*
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
void
|
||||
delete_directory(Directory *directory);
|
||||
void DeleteDirectory(Directory *directory);
|
||||
|
||||
/**
|
||||
* Caller must NOT lock the #db_mutex.
|
||||
*
|
||||
* @return true if the database was modified
|
||||
*/
|
||||
bool
|
||||
delete_name_in(Directory &parent, const char *name);
|
||||
bool DeleteNameIn(Directory &parent, const char *name);
|
||||
|
||||
private:
|
||||
void ClearDirectory(Directory &directory);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,7 +20,7 @@
|
|||
#include "config.h"
|
||||
#include "InotifyQueue.hxx"
|
||||
#include "InotifyDomain.hxx"
|
||||
#include "UpdateGlue.hxx"
|
||||
#include "Service.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -40,7 +40,7 @@ InotifyQueue::OnTimeout()
|
|||
while (!queue.empty()) {
|
||||
const char *uri_utf8 = queue.front().c_str();
|
||||
|
||||
id = update_enqueue(uri_utf8, false);
|
||||
id = update.Enqueue(uri_utf8, false);
|
||||
if (id == 0) {
|
||||
/* retry later */
|
||||
ScheduleSeconds(INOTIFY_UPDATE_DELAY_S);
|
||||
|
|
|
@ -26,11 +26,16 @@
|
|||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class UpdateService;
|
||||
|
||||
class InotifyQueue final : private TimeoutMonitor {
|
||||
UpdateService &update;
|
||||
|
||||
std::list<std::string> queue;
|
||||
|
||||
public:
|
||||
InotifyQueue(EventLoop &_loop):TimeoutMonitor(_loop) {}
|
||||
InotifyQueue(EventLoop &_loop, UpdateService &_update)
|
||||
:TimeoutMonitor(_loop), update(_update) {}
|
||||
|
||||
void Enqueue(const char *uri_utf8);
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ mpd_inotify_callback(int wd, unsigned mask,
|
|||
}
|
||||
|
||||
void
|
||||
mpd_inotify_init(EventLoop &loop, unsigned max_depth)
|
||||
mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth)
|
||||
{
|
||||
LogDebug(inotify_domain, "initializing inotify");
|
||||
|
||||
|
@ -320,7 +320,7 @@ mpd_inotify_init(EventLoop &loop, unsigned max_depth)
|
|||
|
||||
recursive_watch_subdirectories(inotify_root, path, 0);
|
||||
|
||||
inotify_queue = new InotifyQueue(loop);
|
||||
inotify_queue = new InotifyQueue(loop, update);
|
||||
|
||||
LogDebug(inotify_domain, "watching music directory");
|
||||
}
|
||||
|
|
|
@ -24,11 +24,12 @@
|
|||
#include "Compiler.h"
|
||||
|
||||
class EventLoop;
|
||||
class UpdateService;
|
||||
|
||||
#ifdef HAVE_INOTIFY_INIT
|
||||
|
||||
void
|
||||
mpd_inotify_init(EventLoop &loop, unsigned max_depth);
|
||||
mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth);
|
||||
|
||||
void
|
||||
mpd_inotify_finish(void);
|
||||
|
@ -36,7 +37,9 @@ mpd_inotify_finish(void);
|
|||
#else /* !HAVE_INOTIFY_INIT */
|
||||
|
||||
static inline void
|
||||
mpd_inotify_init(gcc_unused EventLoop &loop, gcc_unused unsigned max_depth)
|
||||
mpd_inotify_init(gcc_unused EventLoop &loop,
|
||||
gcc_unused UpdateService &update,
|
||||
gcc_unused unsigned max_depth)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -18,17 +18,10 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "UpdateQueue.hxx"
|
||||
|
||||
#include <queue>
|
||||
#include <list>
|
||||
|
||||
static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32;
|
||||
|
||||
static std::queue<UpdateQueueItem, std::list<UpdateQueueItem>> update_queue;
|
||||
#include "Queue.hxx"
|
||||
|
||||
bool
|
||||
update_queue_push(const char *path, bool discard, unsigned id)
|
||||
UpdateQueue::Push(const char *path, bool discard, unsigned id)
|
||||
{
|
||||
if (update_queue.size() >= MAX_UPDATE_QUEUE_SIZE)
|
||||
return false;
|
||||
|
@ -38,7 +31,7 @@ update_queue_push(const char *path, bool discard, unsigned id)
|
|||
}
|
||||
|
||||
UpdateQueueItem
|
||||
update_queue_shift()
|
||||
UpdateQueue::Pop()
|
||||
{
|
||||
if (update_queue.empty())
|
||||
return UpdateQueueItem();
|
|
@ -23,6 +23,8 @@
|
|||
#include "check.h"
|
||||
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
|
||||
struct UpdateQueueItem {
|
||||
std::string path_utf8;
|
||||
|
@ -39,10 +41,15 @@ struct UpdateQueueItem {
|
|||
}
|
||||
};
|
||||
|
||||
bool
|
||||
update_queue_push(const char *path, bool discard, unsigned id);
|
||||
class UpdateQueue {
|
||||
static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32;
|
||||
|
||||
UpdateQueueItem
|
||||
update_queue_shift();
|
||||
std::queue<UpdateQueueItem, std::list<UpdateQueueItem>> update_queue;
|
||||
|
||||
public:
|
||||
bool Push(const char *path, bool discard, unsigned id);
|
||||
|
||||
UpdateQueueItem Pop();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateRemove.hxx"
|
||||
#include "Remove.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "GlobalEvents.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
|
@ -36,17 +36,12 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
static const Song *removed_song;
|
||||
|
||||
static Mutex remove_mutex;
|
||||
static Cond remove_cond;
|
||||
|
||||
/**
|
||||
* Safely remove a song from the database. This must be done in the
|
||||
* main task, to be sure that there is no pointer left to it.
|
||||
*/
|
||||
static void
|
||||
song_remove_event(void)
|
||||
void
|
||||
UpdateRemoveService::RunDeferred()
|
||||
{
|
||||
assert(removed_song != nullptr);
|
||||
|
||||
|
@ -74,19 +69,13 @@ song_remove_event(void)
|
|||
}
|
||||
|
||||
void
|
||||
update_remove_global_init(void)
|
||||
{
|
||||
GlobalEvents::Register(GlobalEvents::DELETE, song_remove_event);
|
||||
}
|
||||
|
||||
void
|
||||
update_remove_song(const Song *song)
|
||||
UpdateRemoveService::Remove(const Song *song)
|
||||
{
|
||||
assert(removed_song == nullptr);
|
||||
|
||||
removed_song = song;
|
||||
|
||||
GlobalEvents::Emit(GlobalEvents::DELETE);
|
||||
DeferredMonitor::Schedule();
|
||||
|
||||
remove_mutex.lock();
|
||||
|
|
@ -21,18 +21,37 @@
|
|||
#define MPD_UPDATE_REMOVE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "event/DeferredMonitor.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
|
||||
struct Song;
|
||||
|
||||
void
|
||||
update_remove_global_init(void);
|
||||
/**
|
||||
* This class handles #Song removal. It defers the action to the main
|
||||
* thread to ensure that all references to the #Song are gone.
|
||||
*/
|
||||
class UpdateRemoveService final : DeferredMonitor {
|
||||
Mutex remove_mutex;
|
||||
Cond remove_cond;
|
||||
|
||||
const Song *removed_song;
|
||||
|
||||
public:
|
||||
UpdateRemoveService(EventLoop &_loop)
|
||||
:DeferredMonitor(_loop) {}
|
||||
|
||||
/**
|
||||
* Sends a signal to the main thread which will in turn remove the
|
||||
* song: from the sticker database and from the playlist. This
|
||||
* serialized access is implemented to avoid excessive locking.
|
||||
* Sends a signal to the main thread which will in turn remove
|
||||
* the song: from the sticker database and from the playlist.
|
||||
* This serialized access is implemented to avoid excessive
|
||||
* locking.
|
||||
*/
|
||||
void
|
||||
update_remove_song(const Song *song);
|
||||
void Remove(const Song *song);
|
||||
|
||||
private:
|
||||
/* virtual methods from class DeferredMonitor */
|
||||
virtual void RunDeferred() override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -17,12 +17,5 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_INTERNAL_H
|
||||
#define MPD_UPDATE_INTERNAL_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
extern bool walk_discard;
|
||||
extern bool modified;
|
||||
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "Service.hxx"
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_SERVICE_HXX
|
||||
#define MPD_UPDATE_SERVICE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Queue.hxx"
|
||||
#include "Walk.hxx"
|
||||
#include "event/DeferredMonitor.hxx"
|
||||
#include "thread/Thread.hxx"
|
||||
|
||||
/**
|
||||
* This class manages the update queue and runs the update thread.
|
||||
*/
|
||||
class UpdateService final : DeferredMonitor {
|
||||
enum Progress {
|
||||
UPDATE_PROGRESS_IDLE = 0,
|
||||
UPDATE_PROGRESS_RUNNING = 1,
|
||||
UPDATE_PROGRESS_DONE = 2
|
||||
};
|
||||
|
||||
Progress progress;
|
||||
|
||||
bool modified;
|
||||
|
||||
Thread update_thread;
|
||||
|
||||
static const unsigned update_task_id_max = 1 << 15;
|
||||
|
||||
unsigned update_task_id;
|
||||
|
||||
UpdateQueue queue;
|
||||
|
||||
UpdateQueueItem next;
|
||||
|
||||
UpdateWalk walk;
|
||||
|
||||
public:
|
||||
UpdateService(EventLoop &_loop);
|
||||
|
||||
/**
|
||||
* Returns a non-zero job id when we are currently updating
|
||||
* the database.
|
||||
*/
|
||||
unsigned GetId() const {
|
||||
return next.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this path to the database update queue.
|
||||
*
|
||||
* @param path a path to update; if an empty string,
|
||||
* the whole music directory is updated
|
||||
* @return the job id, or 0 on error
|
||||
*/
|
||||
gcc_nonnull_all
|
||||
unsigned Enqueue(const char *path, bool discard);
|
||||
|
||||
private:
|
||||
/* virtual methods from class DeferredMonitor */
|
||||
virtual void RunDeferred() override;
|
||||
|
||||
/* the update thread */
|
||||
void Task();
|
||||
static void Task(void *ctx);
|
||||
|
||||
void StartThread(UpdateQueueItem &&i);
|
||||
|
||||
unsigned GenerateId();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_ARCHIVE_HXX
|
||||
#define MPD_UPDATE_ARCHIVE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct Directory;
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
|
||||
bool
|
||||
update_archive_file(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
#else
|
||||
|
||||
static inline bool
|
||||
update_archive_file(gcc_unused Directory &directory,
|
||||
gcc_unused const char *name,
|
||||
gcc_unused const char *suffix,
|
||||
gcc_unused const struct stat *st)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_CONTAINER_HXX
|
||||
#define MPD_UPDATE_CONTAINER_HXX
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct Directory;
|
||||
struct DecoderPlugin;
|
||||
|
||||
bool
|
||||
update_container_file(Directory &directory,
|
||||
const char *name,
|
||||
const struct stat *st,
|
||||
const char *suffix);
|
||||
|
||||
#endif
|
|
@ -18,15 +18,11 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "UpdateGlue.hxx"
|
||||
#include "UpdateQueue.hxx"
|
||||
#include "UpdateWalk.hxx"
|
||||
#include "UpdateRemove.hxx"
|
||||
#include "Service.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "Mapper.hxx"
|
||||
#include "db/DatabaseSimple.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "GlobalEvents.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "Main.hxx"
|
||||
|
@ -38,30 +34,8 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
static enum update_progress {
|
||||
UPDATE_PROGRESS_IDLE = 0,
|
||||
UPDATE_PROGRESS_RUNNING = 1,
|
||||
UPDATE_PROGRESS_DONE = 2
|
||||
} progress;
|
||||
|
||||
static bool modified;
|
||||
|
||||
static Thread update_thread;
|
||||
|
||||
static const unsigned update_task_id_max = 1 << 15;
|
||||
|
||||
static unsigned update_task_id;
|
||||
|
||||
static UpdateQueueItem next;
|
||||
|
||||
unsigned
|
||||
isUpdatingDB(void)
|
||||
{
|
||||
return next.id;
|
||||
}
|
||||
|
||||
static void
|
||||
update_task(gcc_unused void *ctx)
|
||||
inline void
|
||||
UpdateService::Task()
|
||||
{
|
||||
if (!next.path_utf8.empty())
|
||||
FormatDebug(update_domain, "starting: %s",
|
||||
|
@ -71,7 +45,7 @@ update_task(gcc_unused void *ctx)
|
|||
|
||||
SetThreadIdlePriority();
|
||||
|
||||
modified = update_walk(next.path_utf8.c_str(), next.discard);
|
||||
modified = walk.Walk(next.path_utf8.c_str(), next.discard);
|
||||
|
||||
if (modified || !db_exists()) {
|
||||
Error error;
|
||||
|
@ -86,11 +60,18 @@ update_task(gcc_unused void *ctx)
|
|||
LogDebug(update_domain, "finished");
|
||||
|
||||
progress = UPDATE_PROGRESS_DONE;
|
||||
GlobalEvents::Emit(GlobalEvents::UPDATE);
|
||||
DeferredMonitor::Schedule();
|
||||
}
|
||||
|
||||
static void
|
||||
spawn_update_task(UpdateQueueItem &&i)
|
||||
void
|
||||
UpdateService::Task(void *ctx)
|
||||
{
|
||||
UpdateService &service = *(UpdateService *)ctx;
|
||||
return service.Task();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateService::StartThread(UpdateQueueItem &&i)
|
||||
{
|
||||
assert(main_thread.IsInside());
|
||||
|
||||
|
@ -100,15 +81,15 @@ spawn_update_task(UpdateQueueItem &&i)
|
|||
next = std::move(i);
|
||||
|
||||
Error error;
|
||||
if (!update_thread.Start(update_task, nullptr, error))
|
||||
if (!update_thread.Start(Task, this, error))
|
||||
FatalError(error);
|
||||
|
||||
FormatDebug(update_domain,
|
||||
"spawned thread for update job id %i", next.id);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
generate_update_id()
|
||||
unsigned
|
||||
UpdateService::GenerateId()
|
||||
{
|
||||
unsigned id = update_task_id + 1;
|
||||
if (id > update_task_id_max)
|
||||
|
@ -117,7 +98,7 @@ generate_update_id()
|
|||
}
|
||||
|
||||
unsigned
|
||||
update_enqueue(const char *path, bool discard)
|
||||
UpdateService::Enqueue(const char *path, bool discard)
|
||||
{
|
||||
assert(main_thread.IsInside());
|
||||
|
||||
|
@ -125,16 +106,16 @@ update_enqueue(const char *path, bool discard)
|
|||
return 0;
|
||||
|
||||
if (progress != UPDATE_PROGRESS_IDLE) {
|
||||
const unsigned id = generate_update_id();
|
||||
if (!update_queue_push(path, discard, id))
|
||||
const unsigned id = GenerateId();
|
||||
if (!queue.Push(path, discard, id))
|
||||
return 0;
|
||||
|
||||
update_task_id = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
const unsigned id = update_task_id = generate_update_id();
|
||||
spawn_update_task(UpdateQueueItem(path, discard, id));
|
||||
const unsigned id = update_task_id = GenerateId();
|
||||
StartThread(UpdateQueueItem(path, discard, id));
|
||||
|
||||
idle_add(IDLE_UPDATE);
|
||||
|
||||
|
@ -144,7 +125,8 @@ update_enqueue(const char *path, bool discard)
|
|||
/**
|
||||
* Called in the main thread after the database update is finished.
|
||||
*/
|
||||
static void update_finished_event(void)
|
||||
void
|
||||
UpdateService::RunDeferred()
|
||||
{
|
||||
assert(progress == UPDATE_PROGRESS_DONE);
|
||||
assert(next.IsDefined());
|
||||
|
@ -158,24 +140,16 @@ static void update_finished_event(void)
|
|||
/* send "idle" events */
|
||||
instance->DatabaseModified();
|
||||
|
||||
auto i = update_queue_shift();
|
||||
auto i = queue.Pop();
|
||||
if (i.IsDefined()) {
|
||||
/* schedule the next path */
|
||||
spawn_update_task(std::move(i));
|
||||
StartThread(std::move(i));
|
||||
} else {
|
||||
progress = UPDATE_PROGRESS_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void update_global_init(void)
|
||||
UpdateService::UpdateService(EventLoop &_loop)
|
||||
:DeferredMonitor(_loop), walk(_loop)
|
||||
{
|
||||
GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event);
|
||||
|
||||
update_remove_global_init();
|
||||
update_walk_global_init();
|
||||
}
|
||||
|
||||
void update_global_finish(void)
|
||||
{
|
||||
update_walk_global_finish();
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_GLUE_HXX
|
||||
#define MPD_UPDATE_GLUE_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
void update_global_init(void);
|
||||
|
||||
void update_global_finish(void);
|
||||
|
||||
unsigned
|
||||
isUpdatingDB(void);
|
||||
|
||||
/**
|
||||
* Add this path to the database update queue.
|
||||
*
|
||||
* @param path a path to update; if an empty string,
|
||||
* the whole music directory is updated
|
||||
* @return the job id, or 0 on error
|
||||
*/
|
||||
gcc_nonnull_all
|
||||
unsigned
|
||||
update_enqueue(const char *path, bool discard);
|
||||
|
||||
#endif
|
|
@ -18,11 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateSong.hxx"
|
||||
#include "UpdateInternal.hxx"
|
||||
#include "Service.hxx"
|
||||
#include "UpdateIO.hxx"
|
||||
#include "UpdateDatabase.hxx"
|
||||
#include "UpdateContainer.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/Directory.hxx"
|
||||
|
@ -32,10 +29,10 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
update_song_file2(Directory &directory,
|
||||
const char *name, const struct stat *st,
|
||||
const char *suffix)
|
||||
inline void
|
||||
UpdateWalk::UpdateSongFile2(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st)
|
||||
{
|
||||
db_lock();
|
||||
Song *song = directory.FindSong(name);
|
||||
|
@ -47,7 +44,7 @@ update_song_file2(Directory &directory,
|
|||
directory.GetPath(), name);
|
||||
if (song != nullptr) {
|
||||
db_lock();
|
||||
delete_song(directory, song);
|
||||
editor.DeleteSong(directory, song);
|
||||
db_unlock();
|
||||
}
|
||||
|
||||
|
@ -56,10 +53,10 @@ update_song_file2(Directory &directory,
|
|||
|
||||
if (!(song != nullptr && st->st_mtime == song->mtime &&
|
||||
!walk_discard) &&
|
||||
update_container_file(directory, name, st, suffix)) {
|
||||
UpdateContainerFile(directory, name, suffix, st)) {
|
||||
if (song != nullptr) {
|
||||
db_lock();
|
||||
delete_song(directory, song);
|
||||
editor.DeleteSong(directory, song);
|
||||
db_unlock();
|
||||
}
|
||||
|
||||
|
@ -92,7 +89,7 @@ update_song_file2(Directory &directory,
|
|||
"deleting unrecognized file %s/%s",
|
||||
directory.GetPath(), name);
|
||||
db_lock();
|
||||
delete_song(directory, song);
|
||||
editor.DeleteSong(directory, song);
|
||||
db_unlock();
|
||||
}
|
||||
|
||||
|
@ -101,13 +98,13 @@ update_song_file2(Directory &directory,
|
|||
}
|
||||
|
||||
bool
|
||||
update_song_file(Directory &directory,
|
||||
UpdateWalk::UpdateSongFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st)
|
||||
{
|
||||
if (!decoder_plugins_supports_suffix(suffix))
|
||||
return false;
|
||||
|
||||
update_song_file2(directory, name, st, suffix);
|
||||
UpdateSongFile2(directory, name, suffix, st);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_SONG_HXX
|
||||
#define MPD_UPDATE_SONG_HXX
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct Directory;
|
||||
|
||||
bool
|
||||
update_song_file(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_WALK_HXX
|
||||
#define MPD_UPDATE_WALK_HXX
|
||||
|
||||
#include "check.h"
|
||||
|
||||
void
|
||||
update_walk_global_init(void);
|
||||
|
||||
void
|
||||
update_walk_global_finish(void);
|
||||
|
||||
/**
|
||||
* Returns true if the database was modified.
|
||||
*/
|
||||
bool
|
||||
update_walk(const char *path, bool discard);
|
||||
|
||||
#endif
|
|
@ -18,11 +18,9 @@
|
|||
*/
|
||||
|
||||
#include "config.h" /* must be first for large file support */
|
||||
#include "UpdateWalk.hxx"
|
||||
#include "Walk.hxx"
|
||||
#include "UpdateIO.hxx"
|
||||
#include "UpdateDatabase.hxx"
|
||||
#include "UpdateSong.hxx"
|
||||
#include "UpdateArchive.hxx"
|
||||
#include "Editor.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/DatabaseSimple.hxx"
|
||||
|
@ -49,21 +47,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
bool walk_discard;
|
||||
bool modified;
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true;
|
||||
static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true;
|
||||
|
||||
static bool follow_inside_symlinks;
|
||||
static bool follow_outside_symlinks;
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
update_walk_global_init(void)
|
||||
UpdateWalk::UpdateWalk(EventLoop &_loop)
|
||||
:editor(_loop)
|
||||
{
|
||||
#ifndef WIN32
|
||||
follow_inside_symlinks =
|
||||
|
@ -76,11 +61,6 @@ update_walk_global_init(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
update_walk_global_finish(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
directory_set_stat(Directory &dir, const struct stat *st)
|
||||
{
|
||||
|
@ -89,8 +69,8 @@ directory_set_stat(Directory &dir, const struct stat *st)
|
|||
dir.have_stat = true;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_excluded_from_directory(Directory &directory,
|
||||
inline void
|
||||
UpdateWalk::RemoveExcludedFromDirectory(Directory &directory,
|
||||
const ExcludeList &exclude_list)
|
||||
{
|
||||
db_lock();
|
||||
|
@ -100,7 +80,7 @@ remove_excluded_from_directory(Directory &directory,
|
|||
const auto name_fs = AllocatedPath::FromUTF8(child->GetName());
|
||||
|
||||
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
||||
delete_directory(child);
|
||||
editor.DeleteDirectory(child);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +91,7 @@ remove_excluded_from_directory(Directory &directory,
|
|||
|
||||
const auto name_fs = AllocatedPath::FromUTF8(song->uri);
|
||||
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
||||
delete_song(directory, song);
|
||||
editor.DeleteSong(directory, song);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +99,8 @@ remove_excluded_from_directory(Directory &directory,
|
|||
db_unlock();
|
||||
}
|
||||
|
||||
static void
|
||||
purge_deleted_from_directory(Directory &directory)
|
||||
inline void
|
||||
UpdateWalk::PurgeDeletedFromDirectory(Directory &directory)
|
||||
{
|
||||
Directory *child, *n;
|
||||
directory_for_each_child_safe(child, n, directory) {
|
||||
|
@ -128,7 +108,7 @@ purge_deleted_from_directory(Directory &directory)
|
|||
continue;
|
||||
|
||||
db_lock();
|
||||
delete_directory(child);
|
||||
editor.DeleteDirectory(child);
|
||||
db_unlock();
|
||||
|
||||
modified = true;
|
||||
|
@ -139,7 +119,7 @@ purge_deleted_from_directory(Directory &directory)
|
|||
const auto path = map_song_fs(*song);
|
||||
if (path.IsNull() || !FileExists(path)) {
|
||||
db_lock();
|
||||
delete_song(directory, song);
|
||||
editor.DeleteSong(directory, song);
|
||||
db_unlock();
|
||||
|
||||
modified = true;
|
||||
|
@ -195,8 +175,8 @@ find_inode_ancestor(Directory *parent, ino_t inode, dev_t device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
update_playlist_file2(Directory &directory,
|
||||
inline bool
|
||||
UpdateWalk::UpdatePlaylistFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st)
|
||||
{
|
||||
|
@ -212,30 +192,27 @@ update_playlist_file2(Directory &directory,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
update_regular_file(Directory &directory,
|
||||
inline bool
|
||||
UpdateWalk::UpdateRegularFile(Directory &directory,
|
||||
const char *name, const struct stat *st)
|
||||
{
|
||||
const char *suffix = uri_get_suffix(name);
|
||||
if (suffix == nullptr)
|
||||
return false;
|
||||
|
||||
return update_song_file(directory, name, suffix, st) ||
|
||||
update_archive_file(directory, name, suffix, st) ||
|
||||
update_playlist_file2(directory, name, suffix, st);
|
||||
return UpdateSongFile(directory, name, suffix, st) ||
|
||||
UpdateArchiveFile(directory, name, suffix, st) ||
|
||||
UpdatePlaylistFile(directory, name, suffix, st);
|
||||
}
|
||||
|
||||
static bool
|
||||
update_directory(Directory &directory, const struct stat *st);
|
||||
|
||||
static void
|
||||
update_directory_child(Directory &directory,
|
||||
void
|
||||
UpdateWalk::UpdateDirectoryChild(Directory &directory,
|
||||
const char *name, const struct stat *st)
|
||||
{
|
||||
assert(strchr(name, '/') == nullptr);
|
||||
|
||||
if (S_ISREG(st->st_mode)) {
|
||||
update_regular_file(directory, name, st);
|
||||
UpdateRegularFile(directory, name, st);
|
||||
} else if (S_ISDIR(st->st_mode)) {
|
||||
if (find_inode_ancestor(&directory, st->st_ino, st->st_dev))
|
||||
return;
|
||||
|
@ -246,9 +223,9 @@ update_directory_child(Directory &directory,
|
|||
|
||||
assert(&directory == subdir->parent);
|
||||
|
||||
if (!update_directory(*subdir, st)) {
|
||||
if (!UpdateDirectory(*subdir, st)) {
|
||||
db_lock();
|
||||
delete_directory(subdir);
|
||||
editor.DeleteDirectory(subdir);
|
||||
db_unlock();
|
||||
}
|
||||
} else {
|
||||
|
@ -268,8 +245,9 @@ static bool skip_path(Path path_fs)
|
|||
}
|
||||
|
||||
gcc_pure
|
||||
static bool
|
||||
skip_symlink(const Directory *directory, const char *utf8_name)
|
||||
bool
|
||||
UpdateWalk::SkipSymlink(const Directory *directory,
|
||||
const char *utf8_name) const
|
||||
{
|
||||
#ifndef WIN32
|
||||
const auto path_fs = map_directory_child_fs(*directory, utf8_name);
|
||||
|
@ -333,8 +311,8 @@ skip_symlink(const Directory *directory, const char *utf8_name)
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
update_directory(Directory &directory, const struct stat *st)
|
||||
bool
|
||||
UpdateWalk::UpdateDirectory(Directory &directory, const struct stat *st)
|
||||
{
|
||||
assert(S_ISDIR(st->st_mode));
|
||||
|
||||
|
@ -358,9 +336,9 @@ update_directory(Directory &directory, const struct stat *st)
|
|||
exclude_list.LoadFile(AllocatedPath::Build(path_fs, ".mpdignore"));
|
||||
|
||||
if (!exclude_list.IsEmpty())
|
||||
remove_excluded_from_directory(directory, exclude_list);
|
||||
RemoveExcludedFromDirectory(directory, exclude_list);
|
||||
|
||||
purge_deleted_from_directory(directory);
|
||||
PurgeDeletedFromDirectory(directory);
|
||||
|
||||
while (reader.ReadEntry()) {
|
||||
std::string utf8;
|
||||
|
@ -375,15 +353,15 @@ update_directory(Directory &directory, const struct stat *st)
|
|||
if (utf8.empty())
|
||||
continue;
|
||||
|
||||
if (skip_symlink(&directory, utf8.c_str())) {
|
||||
modified |= delete_name_in(directory, utf8.c_str());
|
||||
if (SkipSymlink(&directory, utf8.c_str())) {
|
||||
modified |= editor.DeleteNameIn(directory, utf8.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat_directory_child(directory, utf8.c_str(), &st2) == 0)
|
||||
update_directory_child(directory, utf8.c_str(), &st2);
|
||||
UpdateDirectoryChild(directory, utf8.c_str(), &st2);
|
||||
else
|
||||
modified |= delete_name_in(directory, utf8.c_str());
|
||||
modified |= editor.DeleteNameIn(directory, utf8.c_str());
|
||||
}
|
||||
|
||||
directory.mtime = st->st_mtime;
|
||||
|
@ -391,8 +369,8 @@ update_directory(Directory &directory, const struct stat *st)
|
|||
return true;
|
||||
}
|
||||
|
||||
static Directory *
|
||||
directory_make_child_checked(Directory &parent, const char *name_utf8)
|
||||
inline Directory *
|
||||
UpdateWalk::DirectoryMakeChildChecked(Directory &parent, const char *name_utf8)
|
||||
{
|
||||
db_lock();
|
||||
Directory *directory = parent.FindChild(name_utf8);
|
||||
|
@ -406,7 +384,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
|||
find_inode_ancestor(&parent, st.st_ino, st.st_dev))
|
||||
return nullptr;
|
||||
|
||||
if (skip_symlink(&parent, name_utf8))
|
||||
if (SkipSymlink(&parent, name_utf8))
|
||||
return nullptr;
|
||||
|
||||
/* if we're adding directory paths, make sure to delete filenames
|
||||
|
@ -414,7 +392,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
|||
db_lock();
|
||||
Song *conflicting = parent.FindSong(name_utf8);
|
||||
if (conflicting)
|
||||
delete_song(parent, conflicting);
|
||||
editor.DeleteSong(parent, conflicting);
|
||||
|
||||
directory = parent.CreateChild(name_utf8);
|
||||
db_unlock();
|
||||
|
@ -423,8 +401,8 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
|||
return directory;
|
||||
}
|
||||
|
||||
static Directory *
|
||||
directory_make_uri_parent_checked(const char *uri)
|
||||
inline Directory *
|
||||
UpdateWalk::DirectoryMakeUriParentChecked(const char *uri)
|
||||
{
|
||||
Directory *directory = db_get_root();
|
||||
char *duplicated = xstrdup(uri);
|
||||
|
@ -436,7 +414,7 @@ directory_make_uri_parent_checked(const char *uri)
|
|||
if (*name_utf8 == 0)
|
||||
continue;
|
||||
|
||||
directory = directory_make_child_checked(*directory,
|
||||
directory = DirectoryMakeChildChecked(*directory,
|
||||
name_utf8);
|
||||
if (directory == nullptr)
|
||||
break;
|
||||
|
@ -448,37 +426,37 @@ directory_make_uri_parent_checked(const char *uri)
|
|||
return directory;
|
||||
}
|
||||
|
||||
static void
|
||||
update_uri(const char *uri)
|
||||
inline void
|
||||
UpdateWalk::UpdateUri(const char *uri)
|
||||
{
|
||||
Directory *parent = directory_make_uri_parent_checked(uri);
|
||||
Directory *parent = DirectoryMakeUriParentChecked(uri);
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
const char *name = PathTraitsUTF8::GetBase(uri);
|
||||
|
||||
struct stat st;
|
||||
if (!skip_symlink(parent, name) &&
|
||||
if (!SkipSymlink(parent, name) &&
|
||||
stat_directory_child(*parent, name, &st) == 0)
|
||||
update_directory_child(*parent, name, &st);
|
||||
UpdateDirectoryChild(*parent, name, &st);
|
||||
else
|
||||
modified |= delete_name_in(*parent, name);
|
||||
modified |= editor.DeleteNameIn(*parent, name);
|
||||
}
|
||||
|
||||
bool
|
||||
update_walk(const char *path, bool discard)
|
||||
UpdateWalk::Walk(const char *path, bool discard)
|
||||
{
|
||||
walk_discard = discard;
|
||||
modified = false;
|
||||
|
||||
if (path != nullptr && !isRootDirectory(path)) {
|
||||
update_uri(path);
|
||||
UpdateUri(path);
|
||||
} else {
|
||||
Directory *directory = db_get_root();
|
||||
struct stat st;
|
||||
|
||||
if (stat_directory(*directory, &st) == 0)
|
||||
update_directory(*directory, &st);
|
||||
UpdateDirectory(*directory, &st);
|
||||
}
|
||||
|
||||
return modified;
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UPDATE_WALK_HXX
|
||||
#define MPD_UPDATE_WALK_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Editor.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct stat;
|
||||
struct Directory;
|
||||
struct archive_plugin;
|
||||
class ExcludeList;
|
||||
|
||||
class UpdateWalk final {
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
friend class UpdateArchiveVisitor;
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true;
|
||||
static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true;
|
||||
|
||||
bool follow_inside_symlinks;
|
||||
bool follow_outside_symlinks;
|
||||
#endif
|
||||
|
||||
bool walk_discard;
|
||||
bool modified;
|
||||
|
||||
DatabaseEditor editor;
|
||||
|
||||
public:
|
||||
UpdateWalk(EventLoop &_loop);
|
||||
|
||||
/**
|
||||
* Returns true if the database was modified.
|
||||
*/
|
||||
bool Walk(const char *path, bool discard);
|
||||
|
||||
private:
|
||||
gcc_pure
|
||||
bool SkipSymlink(const Directory *directory,
|
||||
const char *utf8_name) const;
|
||||
|
||||
void RemoveExcludedFromDirectory(Directory &directory,
|
||||
const ExcludeList &exclude_list);
|
||||
|
||||
void PurgeDeletedFromDirectory(Directory &directory);
|
||||
|
||||
void UpdateSongFile2(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
bool UpdateSongFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
bool UpdateContainerFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
void UpdateArchiveTree(Directory &parent, const char *name);
|
||||
|
||||
bool UpdateArchiveFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
void UpdateArchiveFile(Directory &directory, const char *name,
|
||||
const struct stat *st,
|
||||
const archive_plugin &plugin);
|
||||
|
||||
|
||||
#else
|
||||
bool UpdateArchiveFile(gcc_unused Directory &directory,
|
||||
gcc_unused const char *name,
|
||||
gcc_unused const char *suffix,
|
||||
gcc_unused const struct stat *st) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool UpdatePlaylistFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const struct stat *st);
|
||||
|
||||
bool UpdateRegularFile(Directory &directory,
|
||||
const char *name, const struct stat *st);
|
||||
|
||||
void UpdateDirectoryChild(Directory &directory,
|
||||
const char *name, const struct stat *st);
|
||||
|
||||
bool UpdateDirectory(Directory &directory, const struct stat *st);
|
||||
|
||||
/**
|
||||
* Create the specified directory object if it does not exist
|
||||
* already or if the #stat object indicates that it has been
|
||||
* modified since the last update. Returns nullptr when it
|
||||
* exists already and is unmodified.
|
||||
*
|
||||
* The caller must lock the database.
|
||||
*/
|
||||
Directory *MakeDirectoryIfModified(Directory &parent, const char *name,
|
||||
const struct stat *st);
|
||||
|
||||
Directory *DirectoryMakeChildChecked(Directory &parent,
|
||||
const char *name_utf8);
|
||||
|
||||
Directory *DirectoryMakeUriParentChecked(const char *uri);
|
||||
|
||||
void UpdateUri(const char *uri);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue