Pass a Directory reference to ScanTransaction instead of a directory id.

This is can be done without protecting the directory reference since the method
that removes directories from the watch list is only called on the same thread
as the scan, and never during the life of the ScanTransaction object.
This commit is contained in:
Jim Broadus 2020-01-20 20:12:56 -08:00
parent d15777ea15
commit 52c3ce70ea
2 changed files with 35 additions and 24 deletions

View File

@ -81,8 +81,13 @@ LibraryWatcher::LibraryWatcher(QObject* parent)
connect(rescan_timer_, SIGNAL(timeout()), SLOT(RescanPathsNow())); connect(rescan_timer_, SIGNAL(timeout()), SLOT(RescanPathsNow()));
} }
// Holding a reference to a directory is safe because a ScanTransaction object
// is only created on a stack and the removal of a directory from the watch
// list only occurs as a result of a signal and happens on the watcher's
// thread. So the Directory object will not be deleted out from under us.
LibraryWatcher::ScanTransaction::ScanTransaction(LibraryWatcher* watcher, LibraryWatcher::ScanTransaction::ScanTransaction(LibraryWatcher* watcher,
int dir, bool incremental, const Directory& dir,
bool incremental,
bool ignores_mtime) bool ignores_mtime)
: progress_(0), : progress_(0),
progress_max_(0), progress_max_(0),
@ -125,15 +130,13 @@ LibraryWatcher::ScanTransaction::~ScanTransaction() {
watcher_->task_manager_->SetTaskFinished(task_id_); watcher_->task_manager_->SetTaskFinished(task_id_);
for (const Subdirectory& subdir : deleted_subdirs) { for (const Subdirectory& subdir : deleted_subdirs) {
if (watcher_->watched_dirs_.contains(dir_)) { watcher_->RemoveWatch(dir_, subdir);
watcher_->RemoveWatch(watcher_->watched_dirs_[dir_], subdir);
}
} }
if (watcher_->monitor_) { if (watcher_->monitor_) {
// Watch the new subdirectories // Watch the new subdirectories
for (const Subdirectory& subdir : new_subdirs) { for (const Subdirectory& subdir : new_subdirs) {
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path); watcher_->AddWatch(dir_, subdir.path);
} }
} }
} }
@ -151,7 +154,7 @@ void LibraryWatcher::ScanTransaction::AddToProgressMax(int n) {
SongList LibraryWatcher::ScanTransaction::FindSongsInSubdirectory( SongList LibraryWatcher::ScanTransaction::FindSongsInSubdirectory(
const QString& path) { const QString& path) {
if (cached_songs_dirty_) { if (cached_songs_dirty_) {
cached_songs_ = watcher_->backend_->FindSongsInDirectory(dir_); cached_songs_ = watcher_->backend_->FindSongsInDirectory(dir_id());
cached_songs_dirty_ = false; cached_songs_dirty_ = false;
} }
@ -171,7 +174,7 @@ void LibraryWatcher::ScanTransaction::SetKnownSubdirs(
bool LibraryWatcher::ScanTransaction::HasSeenSubdir(const QString& path) { bool LibraryWatcher::ScanTransaction::HasSeenSubdir(const QString& path) {
if (known_subdirs_dirty_) if (known_subdirs_dirty_)
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_)); SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_id()));
for (const Subdirectory& subdir : known_subdirs_) { for (const Subdirectory& subdir : known_subdirs_) {
if (subdir.path == path && subdir.mtime != 0) return true; if (subdir.path == path && subdir.mtime != 0) return true;
@ -182,7 +185,7 @@ bool LibraryWatcher::ScanTransaction::HasSeenSubdir(const QString& path) {
SubdirectoryList LibraryWatcher::ScanTransaction::GetImmediateSubdirs( SubdirectoryList LibraryWatcher::ScanTransaction::GetImmediateSubdirs(
const QString& path) { const QString& path) {
if (known_subdirs_dirty_) if (known_subdirs_dirty_)
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_)); SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_id()));
SubdirectoryList ret; SubdirectoryList ret;
for (const Subdirectory& subdir : known_subdirs_) { for (const Subdirectory& subdir : known_subdirs_) {
@ -197,25 +200,28 @@ SubdirectoryList LibraryWatcher::ScanTransaction::GetImmediateSubdirs(
SubdirectoryList LibraryWatcher::ScanTransaction::GetAllSubdirs() { SubdirectoryList LibraryWatcher::ScanTransaction::GetAllSubdirs() {
if (known_subdirs_dirty_) if (known_subdirs_dirty_)
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_)); SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_id()));
return known_subdirs_; return known_subdirs_;
} }
void LibraryWatcher::AddDirectory(const Directory& dir, void LibraryWatcher::AddDirectory(const Directory& dir,
const SubdirectoryList& subdirs) { const SubdirectoryList& subdirs) {
watched_dirs_[dir.id] = dir; watched_dirs_[dir.id] = dir;
// Use a reference to our copy of the directory, not the reference to the
// caller's instance.
Directory& new_dir = watched_dirs_[dir.id];
if (subdirs.isEmpty()) { if (subdirs.isEmpty()) {
// This is a new directory that we've never seen before. // This is a new directory that we've never seen before.
// Scan it fully. // Scan it fully.
ScanTransaction transaction(this, dir.id, false); ScanTransaction transaction(this, new_dir, false);
transaction.SetKnownSubdirs(subdirs); transaction.SetKnownSubdirs(subdirs);
transaction.AddToProgressMax(1); transaction.AddToProgressMax(1);
ScanSubdirectory(dir.path, Subdirectory(), &transaction); ScanSubdirectory(new_dir.path, Subdirectory(), &transaction);
} else { } else {
// We can do an incremental scan - looking at the mtimes of each // We can do an incremental scan - looking at the mtimes of each
// subdirectory and only rescan if the directory has changed. // subdirectory and only rescan if the directory has changed.
ScanTransaction transaction(this, dir.id, true); ScanTransaction transaction(this, new_dir, true);
transaction.SetKnownSubdirs(subdirs); transaction.SetKnownSubdirs(subdirs);
transaction.AddToProgressMax(subdirs.count()); transaction.AddToProgressMax(subdirs.count());
for (const Subdirectory& subdir : subdirs) { for (const Subdirectory& subdir : subdirs) {
@ -223,7 +229,7 @@ void LibraryWatcher::AddDirectory(const Directory& dir,
if (scan_on_startup_) ScanSubdirectory(subdir.path, subdir, &transaction); if (scan_on_startup_) ScanSubdirectory(subdir.path, subdir, &transaction);
if (monitor_) AddWatch(dir, subdir.path); if (monitor_) AddWatch(new_dir, subdir.path);
} }
} }
@ -393,7 +399,7 @@ void LibraryWatcher::ScanSubdirectory(const QString& path,
QString image = ImageForSong(file, album_art); QString image = ImageForSong(file, album_art);
for (Song song : song_list) { for (Song song : song_list) {
song.set_directory_id(t->dir()); song.set_directory_id(t->dir_id());
if (song.art_automatic().isEmpty()) song.set_art_automatic(image); if (song.art_automatic().isEmpty()) song.set_art_automatic(image);
t->new_songs << song; t->new_songs << song;
@ -412,7 +418,7 @@ void LibraryWatcher::ScanSubdirectory(const QString& path,
// Add this subdir to the new or touched list // Add this subdir to the new or touched list
Subdirectory updated_subdir; Subdirectory updated_subdir;
updated_subdir.directory_id = t->dir(); updated_subdir.directory_id = t->dir_id();
updated_subdir.mtime = updated_subdir.mtime =
path_info.exists() ? path_info.lastModified().toTime_t() : 0; path_info.exists() ? path_info.lastModified().toTime_t() : 0;
updated_subdir.path = path; updated_subdir.path = path;
@ -456,7 +462,7 @@ void LibraryWatcher::UpdateCueAssociatedSongs(const QString& file,
// update every song that's in the cue and library // update every song that's in the cue and library
for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) { for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
cue_song.set_directory_id(t->dir()); cue_song.set_directory_id(t->dir_id());
Song matching = sections_map[cue_song.beginning_nanosec()]; Song matching = sections_map[cue_song.beginning_nanosec()];
// a new section // a new section
@ -495,7 +501,7 @@ void LibraryWatcher::UpdateNonCueAssociatedSong(const QString& file,
} }
Song song_on_disk; Song song_on_disk;
song_on_disk.set_directory_id(t->dir()); song_on_disk.set_directory_id(t->dir_id());
TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk); TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
if (song_on_disk.is_valid()) { if (song_on_disk.is_valid()) {
@ -678,7 +684,13 @@ void LibraryWatcher::DirectoryChanged(const QString& subdir) {
void LibraryWatcher::RescanPathsNow() { void LibraryWatcher::RescanPathsNow() {
for (int dir : rescan_queue_.keys()) { for (int dir : rescan_queue_.keys()) {
if (stop_requested_) return; if (stop_requested_) return;
ScanTransaction transaction(this, dir, false);
if (!watched_dirs_.contains(dir)) {
qLog(Warning) << "Rescan id" << dir << "not in watch list.";
continue;
}
ScanTransaction transaction(this, watched_dirs_[dir], false);
transaction.AddToProgressMax(rescan_queue_[dir].count()); transaction.AddToProgressMax(rescan_queue_[dir].count());
for (const QString& path : rescan_queue_[dir]) { for (const QString& path : rescan_queue_[dir]) {
@ -825,7 +837,7 @@ void LibraryWatcher::FullScanNow() { PerformScan(false, true); }
void LibraryWatcher::PerformScan(bool incremental, bool ignore_mtimes) { void LibraryWatcher::PerformScan(bool incremental, bool ignore_mtimes) {
for (const Directory& dir : watched_dirs_.values()) { for (const Directory& dir : watched_dirs_.values()) {
ScanTransaction transaction(this, dir.id, incremental, ignore_mtimes); ScanTransaction transaction(this, dir, incremental, ignore_mtimes);
SubdirectoryList subdirs(transaction.GetAllSubdirs()); SubdirectoryList subdirs(transaction.GetAllSubdirs());
transaction.AddToProgressMax(subdirs.count()); transaction.AddToProgressMax(subdirs.count());

View File

@ -88,8 +88,8 @@ signals:
// LibraryBackend::FindSongsInDirectory. // LibraryBackend::FindSongsInDirectory.
class ScanTransaction { class ScanTransaction {
public: public:
ScanTransaction(LibraryWatcher* watcher, int dir, bool incremental, ScanTransaction(LibraryWatcher* watcher, const Directory& dir,
bool ignores_mtime = false); bool incremental, bool ignores_mtime = false);
~ScanTransaction(); ~ScanTransaction();
SongList FindSongsInSubdirectory(const QString& path); SongList FindSongsInSubdirectory(const QString& path);
@ -101,7 +101,7 @@ signals:
void AddToProgress(int n = 1); void AddToProgress(int n = 1);
void AddToProgressMax(int n); void AddToProgressMax(int n);
int dir() const { return dir_; } int dir_id() const { return dir_.id; }
bool is_incremental() const { return incremental_; } bool is_incremental() const { return incremental_; }
bool ignores_mtime() const { return ignores_mtime_; } bool ignores_mtime() const { return ignores_mtime_; }
@ -114,14 +114,13 @@ signals:
SubdirectoryList deleted_subdirs; SubdirectoryList deleted_subdirs;
private: private:
ScanTransaction(const ScanTransaction&) {}
ScanTransaction& operator=(const ScanTransaction&) { return *this; } ScanTransaction& operator=(const ScanTransaction&) { return *this; }
int task_id_; int task_id_;
int progress_; int progress_;
int progress_max_; int progress_max_;
int dir_; const Directory& dir_;
// Incremental scan enters a directory only if it has changed since the // Incremental scan enters a directory only if it has changed since the
// last scan. // last scan.
bool incremental_; bool incremental_;