Cancel scan when directory is removed.

When a directory is removed from the library during a scan, the scan continues
until complete. This change cancels the scan immediately, unblocking the watcher
thread, then signals the watcher to remove the directory.

A second issue occurs when a previously scanned device is removed during a
scan. All remaining files will be marked as deleted. This change mitigates
this issue, but a timing hole still remains here.
This commit is contained in:
Jim Broadus 2020-01-21 00:32:55 -08:00
parent fcd147b574
commit 1c6e43509c
4 changed files with 17 additions and 5 deletions

View File

@ -49,8 +49,9 @@ FilesystemDevice::FilesystemDevice(const QUrl& url, DeviceLister* lister,
connect(backend_.get(),
SIGNAL(DirectoryDiscovered(Directory, SubdirectoryList)), watcher_,
SLOT(AddDirectory(Directory, SubdirectoryList)));
connect(backend_.get(), SIGNAL(DirectoryDeleted(Directory)), watcher_,
SLOT(RemoveDirectory(Directory)));
// RemoveDirectory should be called from the sender's thread.
connect(backend_.get(), &LibraryBackend::DirectoryDeleted, watcher_,
&LibraryWatcher::RemoveDirectory, Qt::DirectConnection);
connect(watcher_, SIGNAL(NewOrUpdatedSongs(SongList)), backend_.get(),
SLOT(AddOrUpdateSongs(SongList)));
connect(watcher_, SIGNAL(SongsMTimeUpdated(SongList)), backend_.get(),

View File

@ -141,8 +141,9 @@ void Library::Init() {
connect(backend_.get(),
SIGNAL(DirectoryDiscovered(Directory, SubdirectoryList)), watcher_,
SLOT(AddDirectory(Directory, SubdirectoryList)));
connect(backend_.get(), SIGNAL(DirectoryDeleted(Directory)), watcher_,
SLOT(RemoveDirectory(Directory)));
// RemoveDirectory should be called from the sender's thread.
connect(backend_.get(), &LibraryBackend::DirectoryDeleted, watcher_,
&LibraryWatcher::RemoveDirectory, Qt::DirectConnection);
connect(backend_.get(), SIGNAL(SongsRatingChanged(SongList)),
SLOT(SongsRatingChanged(SongList)));
connect(backend_.get(), SIGNAL(SongsStatisticsChanged(SongList)),

View File

@ -663,6 +663,12 @@ void LibraryWatcher::RemoveWatch(const Directory& dir,
}
void LibraryWatcher::RemoveDirectory(const Directory& dir) {
watched_dirs_.Stop(dir);
// Invoke the DoRemoveDirectory slot on the watcher's thread.
QMetaObject::invokeMethod(this, "DoRemoveDirectory", Q_ARG(Directory, dir));
}
void LibraryWatcher::DoRemoveDirectory(const Directory& dir) {
rescan_queue_.remove(dir.id);
watched_dirs_.Remove(dir.id);

View File

@ -55,6 +55,10 @@ class LibraryWatcher : public QObject {
void FullScanAsync();
void SetRescanPausedAsync(bool pause);
void ReloadSettingsAsync();
// This thread-safe method will cause a scan of this directory to cancel to
// unblock the watcher thread. It will then invoke the DoRemoveDirectory on
// the watcher's thread to complete the removal.
void RemoveDirectory(const Directory& dir);
void Stop() { watched_dirs_.StopAll(); }
@ -74,7 +78,6 @@ class LibraryWatcher : public QObject {
public slots:
void ReloadSettings();
void AddDirectory(const Directory& dir, const SubdirectoryList& subdirs);
void RemoveDirectory(const Directory& dir);
void SetRescanPaused(bool pause);
private:
@ -158,6 +161,7 @@ class LibraryWatcher : public QObject {
void RescanPathsNow();
void ScanSubdirectory(const QString& path, const Subdirectory& subdir,
ScanTransaction* t, bool force_noincremental = false);
void DoRemoveDirectory(const Directory& dir);
private:
static bool FindSongByPath(const SongList& list, const QString& path,