This necessitated a slight refactoring in the plugin event handling mechanism.
Rather than loading all handlers up front and storing them in a module-scope
structure, we now scan for event handlers at every send(). This is probably
very slightly less efficient but allows for more flexible logic.
This is the crux of the matter. This new lock synchronizes accesses to the
database itself, allowing only one thread to have transactions active at a time.
In effect, this makes sure that beets only has one SQLite transaction active at
a time (and is very conservative in this effort; no read sharing is allowed
either). This prevents all contention in SQLite and hides the pathological
HAVE_SLEEP=0 behavior.
We now properly synchronize access to _tx_stacks and _connections, which can be
concurrently mutated by different threads. This way I don't have to worry about
GIL semantics: DRF => SC!
The Library object is now (almost) safe to share across threads. Per-thread
resources are now automatically managed internally. There is no longer any need
for the _reopen_lib hack to get multiple copies of the Library object.
This essential import pipeline stage is now two: one that applies metadata
changes and one that manipulates the filesystem. This will eventually allow
lastgenere to apply its changes before destinations are calculated.
The shutil.move() function attempts to copy metadata (e.g., permissions and
mtime) when copying a file across filesystems. This always fails on Samba shares
because the utime() call is never permitted by normal users. We don't care about
preserving mtimes across moves, though, so this commit eschews shutil and
reimplements the move algorithm.
Previously, exceptions while communicating with the MusicBrainz API would bring
down the entire autotagging process. These errors are depressingly common, so we
now handle them and log errors to the console (much as we already do with any
exception raised by the Mutagen module). Fault isolation!
This has the added side-effect of giving better context for MB errors when they
do happen -- the logged errors now show the query that was running when MB
failed.