There are several instances of the LibraryModel class used in the system. Each
of these creates a LibraryDirectoryModel instance, but only the instance held
by the main library is every used. Move this out of the LibraryModel class and
into the Library class.
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 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.
When scanning a device such as a mobile phone, it's likely that a directory
containing both image and audio files will be found. Besides the appearance of a
hung scan, it's unlikely that a relevant image will be found.
In the case where filters for relevant filenames don't yield results, set a
sanity limit on the number of images. If the list size is beyond than that
threshold, return an empty path.
On Linux systems, failure to watch a path may be caused by the limit set in
/proc/sys/fs/inotify/max_user_watches. This can be demonstrated by creating
a directory with a large number of empty subdirectories and adding that test
directory as a library.
Check that a file is readable before adding a watch. If adding the watch fails,
report the error to the user only once. Only add the path to subdir_mapping_
if watch succeeds.
A LibraryBackend may be deleted while an associated LibraryModel object is using
it. An example is an async query running while a connected device is removed.
To prevent this, use a share pointer for the LibraryBackend.
This fixes one case where LibraryBackend is used after deletion. However, the
raw pointer is still passed around in several other places. These should be
evaluated on a case-by-case basis to insure that circular depencencies aren't
introduced.
Create a thread pool for each LibraryModel object and block destruction until
all threads that are operating on this object are complete.
Note that this is not a complete solution. The async query also uses the library
backend which may still be deleted before the thread exits. This will be
addressed in a future change.
When unmounting a device, the ConnectedDevice object is destroyed. The
FileSystemDevice destructor waits on its worker thread. If a scan is in
progress, this will block until completion.
There is an existing Stop method in the LibraryWatcher class that is intended to
stop long running operations. To fix, or at least significantly shorten this
hang, we'll call this before waiting for the thread to exit. Also add a
stop_requested check in the cover art scan.
In addition, add a call to Stop in the Library destructor, which has a similar
usage.