Index: src/PlayerInterface.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/PlayerInterface.cs,v
retrieving revision 1.197
diff -u -p -r1.197 PlayerInterface.cs
--- src/PlayerInterface.cs	3 Jul 2006 00:44:00 -0000	1.197
+++ src/PlayerInterface.cs	7 Jul 2006 20:33:36 -0000
@@ -146,7 +146,6 @@ namespace Banshee
 
             DapCore.DapAdded += OnDapCoreDeviceAdded;
             LogCore.Instance.Updated += OnLogCoreUpdated;
-            ImportManager.Instance.ImportRequested += OnImportManagerImportRequested;
             
             InitialLoadTimeout();
             if(!Globals.ArgumentQueue.Contains("hide")) {
@@ -1776,32 +1775,6 @@ namespace Banshee
                 }
             }
         }*/
-       
-        private void OnImportManagerImportRequested(object o, ImportEventArgs args)
-        {
-            try {
-                TrackInfo ti = new LibraryTrackInfo(args.FileName);
-                args.ReturnMessage = String.Format("{0} - {1}", ti.Artist, ti.Title);
-            } catch(Exception e) {
-                args.ReturnMessage = Catalog.GetString("Scanning") + "...";
-                
-                switch(Path.GetExtension(args.FileName)) {
-                    case ".m3u":
-                    case ".jpg":
-                    case ".jpeg":
-                    case ".png":
-                    case ".bmp":
-                    case ".gif":
-                        return;
-                }
-                
-                if(e is ApplicationException) {
-                    return;
-                }
-            
-                Console.WriteLine(Catalog.GetString("Cannot Import: {0} ({1})"), args.FileName, e.GetType());
-            }
-        }
         
         private void DeleteSong(TrackInfo ti)
         {
Index: src/Banshee.Base/Database.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Database.cs,v
retrieving revision 1.7
diff -u -p -r1.7 Database.cs
--- src/Banshee.Base/Database.cs	22 May 2006 17:01:49 -0000	1.7
+++ src/Banshee.Base/Database.cs	7 Jul 2006 20:33:36 -0000
@@ -97,6 +97,12 @@ namespace Banshee.Base
                     ViewOrder INTEGER NOT NULL DEFAULT 0
                 )");
             }
+            
+            // temporary table used during the importing process
+            if(!TableExists("ImportUris")) {
+                Console.WriteLine("Creating ImportUris table");
+                Execute(@"CREATE TABLE ImportUris(Uri TEXT NOT NULL)");
+            }
         }
         
         private void CompatabilityUpdate()
Index: src/Banshee.Base/ImportManager.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/ImportManager.cs,v
retrieving revision 1.12
diff -u -p -r1.12 ImportManager.cs
--- src/Banshee.Base/ImportManager.cs	2 Jul 2006 19:53:43 -0000	1.12
+++ src/Banshee.Base/ImportManager.cs	7 Jul 2006 20:33:37 -0000
@@ -27,8 +27,10 @@
  */
  
 using System;
+using System.Data;
 using System.IO;
 using System.Collections;
+using System.Collections.Generic;
 using System.Threading;
 using Mono.Unix;
 
@@ -67,18 +69,27 @@ namespace Banshee.Base
         private static Gdk.Pixbuf user_event_icon = IconThemeUtils.LoadIcon(22, "system-search", Stock.Find);
         private static readonly object user_event_mutex = new object();
         
-        private Queue path_queue;
+        private Queue<string> path_queue;
         private ActiveUserEvent user_event;
         private int total_count;
         private int processed_count;
         private int scan_ref_count = 0;
-        private bool processing_queue = false;
+        private volatile bool processing_queue = false;
+        
+        private Thread batch_thread = null;
+        private readonly object batch_lock = new object();
+        private static readonly int batch_size = 100;
+        private AutoResetEvent batch_signal = new AutoResetEvent(false);
+        private Queue<KeyValuePair<List<LibraryTrackInfo>,
+                                   List<object>>> batch_queue =
+            new Queue<KeyValuePair<List<LibraryTrackInfo>,
+                                   List<object>>>();
         
         public event ImportEventHandler ImportRequested;
         
         public ImportManager()
         {
-            path_queue = new Queue();
+            path_queue = new Queue<string>();
         }
         
         private void CreateUserEvent()
@@ -138,17 +149,22 @@ namespace Banshee.Base
         {
             path_queue.Clear();
             processing_queue = false;
+            if(batch_thread != null) {
+                batch_signal.Set();
+                batch_thread.Join();
+                batch_thread = null;
+            }
             DestroyUserEvent();
         }
         
         private void Enqueue(string path)
         {
-            if(path_queue.Contains(path)) {
-                return;
-            }
-            
-            total_count++;
-            lock(path_queue.SyncRoot) {
+            lock(path_queue) {
+                if(path_queue.Contains(path)) {
+                    return;
+                }
+                
+                total_count++;
                 path_queue.Enqueue(path);
             }
         }
@@ -170,7 +186,7 @@ namespace Banshee.Base
                 } catch(ImportCanceledException) {
                     FinalizeImport();
                 }
-            });
+            }, true, ThreadPriority.Lowest);
         }
         
         public void QueueSource(Gtk.SelectionData selection)
@@ -229,6 +245,75 @@ namespace Banshee.Base
             scan_ref_count--;
         }
         
+        /// <summary>
+        /// Creates and starts the <see cref="batch_thread"/> if it isn't
+        /// already running.
+        /// </summary>
+        private void EnsureBatchThreadRunning()
+        {
+            if(batch_thread == null) {
+                lock(batch_lock) {
+                    if(batch_thread == null) {
+                        batch_thread = ThreadAssist.Spawn(BatchThreadProc,
+                                                          true,
+                                                          ThreadPriority.Lowest);
+                    }
+                }
+            }
+        }
+        
+        /// <summary>
+        /// The method that the <see cref="batch_thread"/> will be running.
+        /// </summary>
+        private void BatchThreadProc()
+        {
+            KeyValuePair<List<LibraryTrackInfo>, List<object>> batch;
+            
+            while(processing_queue || batch_queue.Count > 0) {
+                batch_signal.WaitOne();
+                
+                while(batch_queue.Count > 0) {
+                    lock(batch_lock) {
+                        batch = batch_queue.Dequeue();
+                    }
+                    
+                    // store the tracks in the database
+                    Globals.Library.Db.QueueTransaction(batch.Value);
+                    
+                    // retrieve their IDs
+                    Dictionary<string, int> track_ids = new Dictionary<string, int>();
+                    string query = @"
+                        SELECT Uri, TrackID
+                        FROM Tracks
+                        WHERE Uri IN (SELECT Uri FROM ImportUris)";
+                    using(IDataReader reader = Globals.Library.Db.Query(query)) {
+                        while(reader.Read()) {
+                            track_ids.Add(reader.GetString(0), reader.GetInt32(1));
+                        }
+                    }
+                    
+                    /* Delete the IDs of the tracks from the current batch from
+                     * the temporary table. Wrapped in a transaction because
+                     * QueueTransaction() doesn't block the calling thread.
+                     */
+                    Globals.Library.Db.QueueTransaction(
+                        new object [] {"DELETE FROM ImportUris"});
+            
+                    /* tell the tracks their IDs and let them notify others that
+                     * they've been added to the database
+                     */
+                    foreach(LibraryTrackInfo track in batch.Key) {
+                        try {
+                            track.SetTrack(track_ids[track.Uri.AbsoluteUri]);
+                        } catch(KeyNotFoundException) {
+                            // this should not happen
+                        }
+                    }
+                }
+            }
+            batch_queue.Clear();
+        }
+        
         private void ProcessQueue()
         {
             if(processing_queue) {
@@ -236,12 +321,64 @@ namespace Banshee.Base
             }
             
             processing_queue = true;
+            List<LibraryTrackInfo> tracks = new List<LibraryTrackInfo>();
+            List<object> commands = new List<object>();
+            object trackCommand;
+            int batch_count = 0;
+            LibraryTrackInfo track;
+            string filename;
             
             while(path_queue.Count > 0) {
                 CheckForCanceled();
                 
-                string filename = path_queue.Dequeue() as string;
+                lock(path_queue) {
+                    filename = path_queue.Dequeue();
+                }
+                    
+                try {
+                    track = new LibraryTrackInfo(filename, out trackCommand);
+                    
+                    if(trackCommand == null) {
+                        continue;
+                    }
+                    
+                    tracks.Add(track);
+                    commands.Add(trackCommand);
+                    commands.Add(string.Format(
+                        @"INSERT INTO ImportUris(Uri) VALUES('{0}')",
+                        Sql.Statement.EscapeQuotes(track.Uri.AbsoluteUri)));
+                    batch_count++;
+                } catch(ApplicationException) {
+                    // Song is already in library
+                    continue;
+                } catch(Exception e) {
+                    UpdateCount(Catalog.GetString("Scanning") + "...");
+                    
+                    switch(Path.GetExtension(filename)) {
+                        case ".m3u":
+                        case ".jpg":
+                        case ".jpeg":
+                        case ".png":
+                        case ".bmp":
+                        case ".gif":
+                            break;
+                        default:
+                            Console.WriteLine(
+                                Catalog.GetString("Cannot Import: {0} ({1})"),
+                                                  filename,
+                                                  e.GetType());
+                            break;
+                    }
                     
+                    continue;
+                }
+                
+                /* MassStorageDap is one of the event handlers for this event.
+                 * It seems like it's queueing files to be copied over to the
+                 * device. I don't have a digital audio player so I don't know
+                 * how these changes would affect one.
+                 * REMOVE THIS COMMENT
+                 */
                 ImportEventHandler handler = ImportRequested;
                 if(handler != null && filename != null) {
                     ImportEventArgs args = new ImportEventArgs();
@@ -249,12 +386,43 @@ namespace Banshee.Base
                     handler(this, args);
                     UpdateCount(args.ReturnMessage);
                 } else {
-                    UpdateCount(null);
+                    UpdateCount(String.Format("{0} - {1}", track.Artist, track.Title));
+                }
+                
+                if(batch_count == batch_size) {
+                    EnsureBatchThreadRunning();
+                    
+                    lock(batch_lock) {
+                        batch_queue.Enqueue(
+                            new KeyValuePair<List<LibraryTrackInfo>,
+                                             List<object>>(tracks, commands));
+                    }
+                    batch_signal.Set();
+                    
+                    batch_count = 0;
+                    tracks = new List<LibraryTrackInfo>();
+                    commands = new List<object>();
                 }
             }
-
+            
+            if(batch_count > 0) {
+                EnsureBatchThreadRunning();
+                    
+                lock(batch_lock) {
+                    batch_queue.Enqueue(
+                        new KeyValuePair<List<LibraryTrackInfo>,
+                                         List<object>>(tracks, commands));
+                }
+                batch_signal.Set();
+            }
+            
             path_queue.Clear();
             processing_queue = false;
+            if(batch_thread != null) {
+                batch_signal.Set();
+                batch_thread.Join();
+                batch_thread = null;
+            }
             
             if(scan_ref_count == 0) {
                 DestroyUserEvent();
Index: src/Banshee.Base/LibraryTrackInfo.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/LibraryTrackInfo.cs,v
retrieving revision 1.12
diff -u -p -r1.12 LibraryTrackInfo.cs
--- src/Banshee.Base/LibraryTrackInfo.cs	14 May 2006 03:22:46 -0000	1.12
+++ src/Banshee.Base/LibraryTrackInfo.cs	7 Jul 2006 20:33:38 -0000
@@ -193,18 +193,37 @@ namespace Banshee.Base
         {
         }
     
-        public LibraryTrackInfo(string filename) : this()
+        public LibraryTrackInfo(string filename, out object command) : this()
         {
             uri = new SafeUri(filename);
             CheckIfExists(uri);
-            if(!LoadFromDatabase(uri)) {
+            /* Is this check necessary? Isn't that what CheckIfExists is for?
+             * REMOVE THIS COMMENT
+             */
+            //if(!LoadFromDatabase(uri)) {
                 LoadFromFile(filename);
+                
                 string new_filename = MoveToPlace(filename, true);
-                uri = new SafeUri(new_filename != null ? new_filename : filename);
-                CheckIfExists(uri);
-                SaveToDatabase(true);
-            }
-
+                
+                if(new_filename != null && new_filename != filename) {
+                    uri = new SafeUri(new_filename);
+                    CheckIfExists(uri);
+                }
+                
+                command = GetInsertStatement();
+            //} else {
+            //    command = null;
+            //}
+        }
+        
+        /// <summary>
+        /// Called by the <see cref="Banshee.Base.ImportManager"/> after the
+        /// track has been added to the database if the
+        /// <see cref="LibraryTrackInfo"/> object was created with a file name.
+        /// </summary>
+        public void SetTrack(int trackId)
+        {
+            track_id = trackId;
             Globals.Library.SetTrack(track_id, this);
 
             PreviousTrack = Gtk.TreeIter.Zero;
@@ -284,6 +303,71 @@ namespace Banshee.Base
                 title = /*"Unknown Title"*/ null;
         }
         
+        /// <summary>
+        /// Returns the SQL statement used to INSERT this track into the
+        /// database.
+        /// </summary>
+        private Sql.Statement GetInsertStatement()
+        {
+            return new Insert(
+                "Tracks", true,
+                "TrackID", null, 
+                "Uri", uri.AbsoluteUri,
+                "MimeType", mimetype, 
+                "Artist", artist, 
+                "Performer", performer, 
+                "AlbumTitle", album,
+                "ASIN", asin,
+                "Label", label,
+                "Title", title, 
+                "Genre", genre, 
+                "Year", year,
+                "DateAddedStamp", DateTimeUtil.FromDateTime(date_added), 
+                "TrackNumber", track_number, 
+                "TrackCount", track_count, 
+                "Duration", (int)duration.TotalSeconds, 
+                "TrackGain", track_gain, 
+                "TrackPeak", track_peak, 
+                "AlbumGain", album_gain, 
+                "AlbumPeak", album_peak, 
+                "Rating", rating, 
+                "NumberOfPlays", play_count, 
+                "LastPlayedStamp", DateTimeUtil.FromDateTime(last_played),
+                "RemoteLookupStatus", (int)remote_lookup_status);
+        }
+        
+        /// <summary>
+        /// Returns the SQL statement used to UPDATE this track in the database.
+        /// </summary>
+        private Sql.Statement GetUpdateStatement()
+        {
+            return new Update("Tracks",
+                "Uri", uri.AbsoluteUri,
+                "MimeType", mimetype, 
+                "Artist", artist, 
+                "Performer", performer, 
+                "AlbumTitle", album,
+                "ASIN", asin,
+                "Label", label,
+                "Title", title, 
+                "Genre", genre, 
+                "Year", year,
+                "DateAddedStamp", DateTimeUtil.FromDateTime(date_added), 
+                "TrackNumber", track_number, 
+                "TrackCount", track_count, 
+                "Duration", (int)duration.TotalSeconds, 
+                "TrackGain", track_gain, 
+                "TrackPeak", track_peak, 
+                "AlbumGain", album_gain, 
+                "AlbumPeak", album_peak, 
+                "Rating", rating, 
+                "NumberOfPlays", play_count, 
+                "LastPlayedStamp", DateTimeUtil.FromDateTime(last_played),
+                "RemoteLookupStatus", (int)remote_lookup_status) +
+                new Where(new Compare("TrackID", Op.EqualTo, track_id));// +
+                //    new Limit(1);
+        }
+        
         private void SaveToDatabase(bool retryIfFail)
         {
             Statement tracksQuery;
@@ -291,60 +375,17 @@ namespace Banshee.Base
 //            Console.WriteLine ("{0} has id {1}", uri.LocalPath, TrackId);
 
             if(track_id <= 0) {
-                tracksQuery = new Insert("Tracks", true,
-                    "TrackID", null, 
-                    "Uri", uri.AbsoluteUri,
-                    "MimeType", mimetype, 
-                    "Artist", artist, 
-                    "Performer", performer, 
-                    "AlbumTitle", album,
-                    "ASIN", asin,
-                    "Label", label,
-                    "Title", title, 
-                    "Genre", genre, 
-                    "Year", year,
-                    "DateAddedStamp", DateTimeUtil.FromDateTime(date_added), 
-                    "TrackNumber", track_number, 
-                    "TrackCount", track_count, 
-                    "Duration", (int)duration.TotalSeconds, 
-                    "TrackGain", track_gain, 
-                    "TrackPeak", track_peak, 
-                    "AlbumGain", album_gain, 
-                    "AlbumPeak", album_peak, 
-                    "Rating", rating, 
-                    "NumberOfPlays", play_count, 
-                    "LastPlayedStamp", DateTimeUtil.FromDateTime(last_played),
-                    "RemoteLookupStatus", (int)remote_lookup_status);
+                tracksQuery = GetInsertStatement();
             } else {
-                tracksQuery = new Update("Tracks",
-                    "Uri", uri.AbsoluteUri,
-                    "MimeType", mimetype, 
-                    "Artist", artist, 
-                    "Performer", performer, 
-                    "AlbumTitle", album,
-                    "ASIN", asin,
-                    "Label", label,
-                    "Title", title, 
-                    "Genre", genre, 
-                    "Year", year,
-                    "DateAddedStamp", DateTimeUtil.FromDateTime(date_added), 
-                    "TrackNumber", track_number, 
-                    "TrackCount", track_count, 
-                    "Duration", (int)duration.TotalSeconds, 
-                    "TrackGain", track_gain, 
-                    "TrackPeak", track_peak, 
-                    "AlbumGain", album_gain, 
-                    "AlbumPeak", album_peak, 
-                    "Rating", rating, 
-                    "NumberOfPlays", play_count, 
-                    "LastPlayedStamp", DateTimeUtil.FromDateTime(last_played),
-                    "RemoteLookupStatus", (int)remote_lookup_status) +
-                    new Where(new Compare("TrackID", Op.EqualTo, track_id));// +
-                //    new Limit(1);
+                tracksQuery = GetUpdateStatement();
             }
             
             try {
-                Globals.Library.Db.Execute(tracksQuery);
+                /* This should be the optimization for the GetId() at the end
+                 * of this method - Execute() returns the last insert id.
+                 * REMOVE THIS COMMENT
+                 */
+                track_id = Globals.Library.Db.Execute(tracksQuery);
             } catch(Exception e) {
                 Console.WriteLine(e);
             }
@@ -353,11 +394,12 @@ namespace Banshee.Base
                 track_id = 0;
                 SaveToDatabase(false);
             } else if(track_id <= 0) {*/
-            
-            if(track_id <= 0) 
-               track_id = GetId(uri); /* OPTIMIZE! Seems like an unnecessary query */
         }
         
+        /* never used right now
+         * REMOVE THIS COMMENT
+         */
+        /*
         private bool LoadFromDatabase(object id)
         {
             string query = String.Format(@"
@@ -373,13 +415,18 @@ namespace Banshee.Base
             if(reader == null)
                 return false;
             
-            if(!reader.Read())
-                return false;
+            try {
+                if(!reader.Read())
+                    return false;
                 
-            LoadFromDatabaseReader(reader);
+                LoadFromDatabaseReader(reader);
+            } finally {
+                reader.Close();
+            }
             
             return true;
         }
+        */
         
         private void LoadFromDatabaseReader(IDataReader reader)
         {
Index: src/Banshee.Base/QueuedSqliteDatabase.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/QueuedSqliteDatabase.cs,v
retrieving revision 1.6
diff -u -p -r1.6 QueuedSqliteDatabase.cs
--- src/Banshee.Base/QueuedSqliteDatabase.cs	2 Jul 2006 21:35:30 -0000	1.6
+++ src/Banshee.Base/QueuedSqliteDatabase.cs	7 Jul 2006 20:33:39 -0000
@@ -27,6 +27,7 @@
  */
  
 using System;
+using System.Data;
 using System.Threading;
 using System.Collections.Generic;
 using Mono.Data.SqliteClient;
@@ -35,13 +36,20 @@ namespace Banshee.Database
 {
     public class QueuedSqliteDatabase : IDisposable
     {
-        private Queue<QueuedSqliteCommand> command_queue = new Queue<QueuedSqliteCommand>();
+        private Queue<List<QueuedSqliteCommand>> command_queue =
+            new Queue<List<QueuedSqliteCommand>>();
+        
+        /// <summary>
+        /// Signal used to indicate there is new data in the
+        /// <see cref="command_queue"/>.
+        /// </summary>
+        private AutoResetEvent queue_signal = new AutoResetEvent(false);
+        
         private SqliteConnection connection;
         private Thread queue_thread;
-        private bool dispose_requested = false;
-        private bool processing_queue = false;
+        private volatile bool dispose_requested = false;
         private string dbpath;
-        private bool connected;
+        private volatile bool connected;
         
         public QueuedSqliteDatabase(string dbpath)
         {
@@ -54,8 +62,8 @@ namespace Banshee.Database
         public void Dispose()
         {
             dispose_requested = true;
-            WakeUp();
-            while(processing_queue);
+            queue_signal.Set();
+            queue_thread.Join();
         }
         
         private void WaitForConnection()
@@ -65,10 +73,51 @@ namespace Banshee.Database
 
         private void QueueCommand(QueuedSqliteCommand command)
         {
+            List<QueuedSqliteCommand> list = new List<QueuedSqliteCommand>();
+            
+            list.Add(command);
+            
+            lock(command_queue) {
+                command_queue.Enqueue(list);
+            }
+            queue_signal.Set();
+        }
+        
+        /// <summary>
+        /// Queues a set of commands to be executed together as a transaction.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// The commands can be any object whose ToString() method will return
+        /// the SQL statement to be executed.
+        /// </para>
+        /// <para>
+        /// The method will return immediately and the transaction will execute
+        /// as soon as all the commands that are queued before it have executed.
+        /// </para>
+        /// </remarks>
+        public void QueueTransaction(IEnumerable<object> commands)
+        {
+            List<QueuedSqliteCommand> transaction;
+            
+            try {
+                transaction = (List<QueuedSqliteCommand>)commands;
+            } catch {
+                transaction = new List<QueuedSqliteCommand>();
+                
+                foreach(object command in commands) {
+                    transaction.Add(
+                        new QueuedSqliteCommand(
+                            connection,
+                            command.ToString(),
+                            Banshee.Database.CommandType.Execute));
+                }
+            }
+            
             lock(command_queue) {
-                command_queue.Enqueue(command);
+                command_queue.Enqueue(transaction);
             }
-            WakeUp();
+            queue_signal.Set();
         }
         
         public SqliteDataReader Query(object query)
@@ -107,13 +156,6 @@ namespace Banshee.Database
                     WHERE Type='table' AND Name='{0}'", 
                     table))) > 0;
         }
-
-        private void WakeUp()
-        {
-            Monitor.Enter(command_queue);
-            Monitor.Pulse(command_queue);
-            Monitor.Exit(command_queue);
-        }
         
         private void ProcessQueue()
         {         
@@ -123,35 +165,33 @@ namespace Banshee.Database
                 connected = true;
             }
             
-            processing_queue = true;
-            bool in_dispose_transaction = false;
+            List<QueuedSqliteCommand> commands;
             
             while(true) {
                 while(command_queue.Count > 0) {
-                    if(dispose_requested && !in_dispose_transaction) {
-                        (new SqliteCommand("BEGIN", connection)).ExecuteNonQuery();
-                        in_dispose_transaction = true;
+                    lock(command_queue) {
+                        commands = command_queue.Dequeue();
                     }
                     
-                    QueuedSqliteCommand command;
-                    lock(command_queue) {
-                        command = command_queue.Dequeue();
+                    if(commands.Count == 1) {
+                        commands[0].Execute();
+                        continue;
+                    }
+                    
+                    using(IDbTransaction t = connection.BeginTransaction()) {
+                        foreach(QueuedSqliteCommand command in commands) {
+                            command.Execute();
+                        }
+                        t.Commit();
                     }
-                    command.Execute();
                 }
-
+                
                 if(dispose_requested) {
-                    if(in_dispose_transaction) {
-                        (new SqliteCommand("COMMIT", connection)).ExecuteNonQuery();
-                    }
                     connection.Close();
-                    processing_queue = false;
                     return;
                 }
                 
-                Monitor.Enter(command_queue);
-                Monitor.Wait(command_queue);
-                Monitor.Exit(command_queue);
+                queue_signal.WaitOne();
             }
         }
     }
@@ -168,7 +208,7 @@ namespace Banshee.Database
         private object result;
         private int insert_id;
         private Exception execution_exception;
-        private bool finished = false;
+        private volatile bool finished = false;
         
         public QueuedSqliteCommand(SqliteConnection connection, string command, CommandType commandType) 
             : base(command, connection)
Index: src/Banshee.Base/Utilities.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Utilities.cs,v
retrieving revision 1.20
diff -u -p -r1.20 Utilities.cs
--- src/Banshee.Base/Utilities.cs	21 Jun 2006 05:55:52 -0000	1.20
+++ src/Banshee.Base/Utilities.cs	7 Jul 2006 20:33:39 -0000
@@ -245,19 +245,37 @@ namespace Banshee.Base
             }
         }
         
-        public static Thread Spawn(ThreadStart threadedMethod, bool autoStart)
+        /// <summary>
+        /// Spawns and returns a new background thread.
+        /// </summary>
+        public static Thread Spawn(ThreadStart threadedMethod,
+                                   bool autoStart,
+                                   ThreadPriority priority)
         {
-            Thread thread = new Thread(threadedMethod);
+            Thread thread = new Thread(delegate {
+                try {
+                    threadedMethod();
+                } catch(Exception e) {
+                    Console.Error.WriteLine("[Uncaught thread exception]: {0}", e);
+                }
+            });
             thread.IsBackground = true;
             if(autoStart) {
                 thread.Start();
             }
+            thread.Priority = priority;
             return thread;
         }
         
+        public static Thread Spawn(ThreadStart threadedMethod, bool autoStart)
+        {
+            return Spawn(threadedMethod, autoStart, ThreadPriority.Normal);
+        }
+        
+        
         public static Thread Spawn(ThreadStart threadedMethod)
         {
-            return Spawn(threadedMethod, true);
+            return Spawn(threadedMethod, true, ThreadPriority.Normal);
         }
     }
     
