banshee r4967 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Database src/Core/Banshee.Services/Banshee.Playlist src/Extensions/Banshee.Daap/Banshee.Daap src/Libraries/Hyena/Hyena.Data.Sqlite



Author: gburt
Date: Mon Jan 26 04:24:37 2009
New Revision: 4967
URL: http://svn.gnome.org/viewvc/banshee?rev=4967&view=rev

Log:
2009-01-25  Gabriel Burt  <gabriel burt gmail com>

	* Core/Banshee.Services/Banshee.Collection.Database/DatabaseFilterListModel.cs:
	* Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs:
	* Core/Banshee.Services/Banshee.Collection/FilterListModel.cs:
	* Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs:
	* Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs:
	* Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs:
	* Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs:
	* Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs:
	* Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs: Fix the
	Begin/Commit/RollbackTransaction methods in HyenaSqliteConnection.  Use
	transactions when importing media, loading DAAP shares, and reloading
	filter models.

Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseFilterListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/FilterListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
   trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseFilterListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseFilterListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseFilterListModel.cs	Mon Jan 26 04:24:37 2009
@@ -49,7 +49,7 @@
         
         private long count;
         private string reload_fragment;
-        
+
         private string reload_fragment_format;
         protected string ReloadFragmentFormat {
             get { return reload_fragment_format; }
@@ -57,6 +57,7 @@
         }
         
         protected readonly U select_all_item;
+        private HyenaSqliteConnection connection;
 
         public DatabaseFilterListModel (string name, string label, Banshee.Sources.DatabaseSource source, 
                                         DatabaseTrackListModel trackModel, HyenaSqliteConnection connection, SqliteModelProvider<T> provider, U selectAllItem, string uuid) 
@@ -67,6 +68,7 @@
             FilterLabel = label;
             select_all_item = selectAllItem;
             
+            this.connection = connection;
             cache = new BansheeModelCache <T> (connection, uuid, this, provider);
             cache.HasSelectAllItem = true;
         }
@@ -148,12 +150,16 @@
         {
             GenerateReloadFragment ();
 
-            cache.SaveSelection ();
-            cache.Reload ();
-            cache.UpdateAggregates ();
-            cache.RestoreSelection ();
+            lock (cache) {
+                connection.BeginTransaction ();
+                cache.SaveSelection ();
+                cache.Reload ();
+                cache.UpdateAggregates ();
+                cache.RestoreSelection ();
+                connection.CommitTransaction ();
 
-            count = cache.Count + 1;
+                count = cache.Count + 1;
+            }
             
             UpdateSelectAllItem (count - 1);
 
@@ -166,7 +172,9 @@
                 if (index == 0)
                     return select_all_item;
 
-                return cache.GetValue (index - 1);
+                lock (cache) {
+                    return cache.GetValue (index - 1);
+                }
             }
         }
 
@@ -187,7 +195,11 @@
         }
 
         public int CacheId {
-            get { return (int) cache.CacheId; }
+            get {
+                lock (cache) {
+                    return (int) cache.CacheId;
+                }
+            }
         }
         
         public IEnumerable<object> GetSelectedObjects ()
@@ -199,7 +211,9 @@
 
         public override void InvalidateCache (bool notify)
         {
-            cache.Clear ();
+            lock (cache) {
+                cache.Clear ();
+            }
             if (notify) {
                 OnReloaded ();
             }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs	Mon Jan 26 04:24:37 2009
@@ -169,7 +169,7 @@
 
             // TODO note, there is deadlock potential here b/c of locking of shared commands and blocking
             // because of transactions.  Needs to be fixed in HyenaDatabaseConnection.
-            //ServiceManager.DbConnection.BeginTransaction ();
+            ServiceManager.DbConnection.BeginTransaction ();
             try {
                 track = new DatabaseTrackInfo ();
                 track.Uri = uri;
@@ -183,9 +183,9 @@
 
                 track.Save (false);
 
-                //ServiceManager.DbConnection.CommitTransaction ();
+                ServiceManager.DbConnection.CommitTransaction ();
             } catch (Exception) {
-                //ServiceManager.DbConnection.RollbackTransaction ();
+                ServiceManager.DbConnection.RollbackTransaction ();
                 throw;
             }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/FilterListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/FilterListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/FilterListModel.cs	Mon Jan 26 04:24:37 2009
@@ -70,6 +70,11 @@
         
         private void HandleSelectionChanged (object sender, EventArgs args)
         {
+            Banshee.Base.ThreadAssist.SpawnFromMain (ReloadBrowsingModel);
+        } 
+
+        private void ReloadBrowsingModel ()
+        {
             browsing_model.Reload (this);
         }
         

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs	Mon Jan 26 04:24:37 2009
@@ -62,6 +62,7 @@
             
             if (Banshee.Base.ApplicationContext.CommandLine.Contains ("debug-sql")) {
                 Hyena.Data.Sqlite.HyenaSqliteCommand.LogAll = true;
+                WarnIfCalledFromThread = Banshee.Base.ThreadAssist.MainThread;
             }
         }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	Mon Jan 26 04:24:37 2009
@@ -277,12 +277,13 @@
             HyenaSqliteCommand select_command = new HyenaSqliteCommand (String.Format ("SELECT ItemID FROM CoreCache WHERE ModelID = {0} LIMIT ?, ?", DatabaseTrackModel.CacheId));
             
             // Reorder the selected items
-            // TODO put in transaction
+            ServiceManager.DbConnection.BeginTransaction ();
             foreach (RangeCollection.Range range in TrackModel.Selection.Ranges) {
                 foreach (long entry_id in ServiceManager.DbConnection.QueryEnumerable<long> (select_command, range.Start, range.Count)) {
                     ServiceManager.DbConnection.Execute (update_command, order++, entry_id);
                 }
             }
+            ServiceManager.DbConnection.CommitTransaction ();
             
             Reload ();
         }
@@ -360,12 +361,12 @@
         {
             if (!temps_cleared) {
                 temps_cleared = true;
+                ServiceManager.DbConnection.BeginTransaction ();
                 ServiceManager.DbConnection.Execute (@"
-                    BEGIN TRANSACTION;
-                        DELETE FROM CorePlaylistEntries WHERE PlaylistID IN (SELECT PlaylistID FROM CorePlaylists WHERE IsTemporary = 1);
-                        DELETE FROM CorePlaylists WHERE IsTemporary = 1;
-                    COMMIT TRANSACTION"
+                    DELETE FROM CorePlaylistEntries WHERE PlaylistID IN (SELECT PlaylistID FROM CorePlaylists WHERE IsTemporary = 1);
+                    DELETE FROM CorePlaylists WHERE IsTemporary = 1;"
                 );
+                ServiceManager.DbConnection.CommitTransaction ();
             }
         }
         

Modified: trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs	Mon Jan 26 04:24:37 2009
@@ -31,6 +31,7 @@
 using Mono.Unix;
 
 using Hyena;
+using Hyena.Data.Sqlite;
 
 using Banshee.Base;
 using Banshee.Collection;
@@ -243,14 +244,23 @@
                     notify_every -= notify_every % 250;
                     
                     int count = 0;
-                    // TODO use transactions when fixed
                     DaapTrackInfo daap_track = null;
+
+                    HyenaSqliteConnection conn = ServiceManager.DbConnection;
+                    conn.BeginTransaction ();
                     foreach (DAAP.Track track in database.Tracks) {
                         daap_track = new DaapTrackInfo (track, this);
                         
                         // Only notify once in a while because otherwise the source Reloading slows things way down
-                        daap_track.Save (++count % notify_every == 0);
+                        if (++count % notify_every == 0) {
+                            conn.CommitTransaction ();
+                            daap_track.Save (true);
+                            conn.BeginTransaction ();
+                        } else {
+                            daap_track.Save (false);
+                        }
                     }
+                    conn.CommitTransaction ();
                     
                     // Save the last track once more to trigger the NotifyTrackAdded
                     if (daap_track != null) {

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	Mon Jan 26 04:24:37 2009
@@ -58,10 +58,11 @@
     
     public class HyenaSqliteCommand
     {
-        protected object result = null;
+        private object result = null;
         private Exception execution_exception = null;
         private bool finished = false;
 
+        private ManualResetEvent finished_event = new ManualResetEvent (true);
         private string command;
         private string command_format = null;
         private string command_formatted = null;
@@ -149,6 +150,7 @@
                 execution_exception = e;
             }
 
+            finished_event.Reset ();
             finished = true;
         }
 
@@ -164,6 +166,7 @@
             finished = false;
 
             conn.ClaimResult ();
+            finished_event.Set ();
 
             if (execution_exception != null) {
                 throw execution_exception;
@@ -172,6 +175,11 @@
             return ret;
         }
 
+        internal void WaitIfNotFinished ()
+        {
+            finished_event.WaitOne ();
+        }
+
         internal HyenaSqliteCommand ApplyValues (params object [] param_values)
         {
             if (command_format == null) {
@@ -201,7 +209,7 @@
             return this;
         }
 
-        protected static object SqlifyObject (object o)
+        private static object SqlifyObject (object o)
         {
             if (o is string) {
                 return String.Format ("'{0}'", (o as string).Replace ("'", "''"));

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	Mon Jan 26 04:24:37 2009
@@ -90,7 +90,12 @@
         private SqliteConnection connection;
         private string dbpath;
 
+        // These are 'parallel' queues; that is, when a value is pushed or popped to
+        // one, a value is pushed or popped to all three.
         private Queue<HyenaSqliteCommand> command_queue = new Queue<HyenaSqliteCommand>();
+        private Queue<object[]> args_queue = new Queue<object[]>();
+        private Queue<object> arg_queue = new Queue<object>();
+
         private Thread queue_thread;
         private volatile bool dispose_requested = false;
         private volatile int results_ready = 0;
@@ -100,6 +105,12 @@
         private volatile Thread transaction_thread = null;
         private ManualResetEvent transaction_signal = new ManualResetEvent (true);
 
+        private Thread warn_if_called_from_thread;
+        public Thread WarnIfCalledFromThread {
+            get { return warn_if_called_from_thread; }
+            set { warn_if_called_from_thread = value; }
+        }
+
         internal ManualResetEvent ResultReadySignal {
             get { return result_ready_signal; }
         }
@@ -110,6 +121,7 @@
         {
             this.dbpath = dbpath;
             queue_thread = new Thread(ProcessQueue);
+            queue_thread.Name = String.Format ("HyenaSqliteConnection ({0})", dbpath);
             queue_thread.IsBackground = true;
             queue_thread.Start();
         }
@@ -121,18 +133,16 @@
         // SELECT multiple column queries
         public IDataReader Query (HyenaSqliteCommand command)
         {
-            lock (command) {
-                command.CommandType = HyenaCommandType.Reader;
-                QueueCommand (command);
-                return command.WaitForResult (this) as SqliteDataReader;
-            }
+            command.CommandType = HyenaCommandType.Reader;
+            QueueCommand (command, null);
+            return command.WaitForResult (this) as SqliteDataReader;
         }
 
         public IDataReader Query (HyenaSqliteCommand command, params object [] param_values)
         {
-            lock (command) {
-                return Query (command.ApplyValues (param_values));
-            }
+            command.CommandType = HyenaCommandType.Reader;
+            QueueCommand (command, param_values);
+            return command.WaitForResult (this) as SqliteDataReader;
         }
 
         public IDataReader Query (string command_str, params object [] param_values)
@@ -158,8 +168,11 @@
 
         public IEnumerable<T> QueryEnumerable<T> (HyenaSqliteCommand command, params object [] param_values)
         {
-            lock (command) {
-                return QueryEnumerable<T> (command.ApplyValues (param_values));
+            Type type = typeof (T);
+            using (IDataReader reader = Query (command, param_values)) {
+                while (reader.Read ()) {
+                    yield return (T) SqliteUtils.FromDbFormat (type, reader[0]);
+                }
             }
         }
 
@@ -176,21 +189,18 @@
         // SELECT single column, single row queries
         public T Query<T> (HyenaSqliteCommand command)
         {
-            object result = null;
-            lock (command) {
-                command.CommandType = HyenaCommandType.Scalar;
-                QueueCommand (command);
-                result = command.WaitForResult (this);
-            }
-
+            command.CommandType = HyenaCommandType.Scalar;
+            QueueCommand (command, null);
+            object result = command.WaitForResult (this);
             return (T)SqliteUtils.FromDbFormat (typeof (T), result);
         }
 
         public T Query<T> (HyenaSqliteCommand command, params object [] param_values)
         {
-            lock (command) {
-                return Query<T> (command.ApplyValues (param_values));
-            }
+            command.CommandType = HyenaCommandType.Scalar;
+            QueueCommand (command, param_values);
+            object result = command.WaitForResult (this);
+            return (T)SqliteUtils.FromDbFormat (typeof (T), result);
         }
 
         public T Query<T> (string command_str, params object [] param_values)
@@ -206,18 +216,16 @@
         // INSERT, UPDATE, DELETE queries
         public int Execute (HyenaSqliteCommand command)
         {
-            lock (command) {
-                command.CommandType = HyenaCommandType.Execute;;
-                QueueCommand(command);
-                return (int) command.WaitForResult (this);
-            }
+            command.CommandType = HyenaCommandType.Execute;;
+            QueueCommand(command, null);
+            return (int) command.WaitForResult (this);
         }
 
         public int Execute (HyenaSqliteCommand command, params object [] param_values)
         {
-            lock (command) {
-                return Execute (command.ApplyValues (param_values));
-            }
+            command.CommandType = HyenaCommandType.Execute;;
+            QueueCommand(command, param_values);
+            return (int) command.WaitForResult (this);
         }
 
         public int Execute (string command_str, params object [] param_values)
@@ -245,7 +253,7 @@
         // the command locking and values applied only when we know the calling thread is not blocking by a
         // transaction thread.
         //
-        /*public void BeginTransaction ()
+        public void BeginTransaction ()
         {
             if (transaction_thread == Thread.CurrentThread) {
                 throw new Exception ("Can't start a recursive transaction");
@@ -297,7 +305,7 @@
                 // Let any other threads continue
                 transaction_signal.Set (); 
             }
-        }*/
+        }
 
         public bool TableExists (string tableName)
         {
@@ -365,12 +373,28 @@
 
 #region Private Queue Methods
 
-        private void QueueCommand(HyenaSqliteCommand command)
+        private void QueueCommand(HyenaSqliteCommand command, object [] args)
         {
+            QueueCommand (command, null, args);
+        }
+
+        private void QueueCommand(HyenaSqliteCommand command, object arg)
+        {
+            QueueCommand (command, arg, null);
+        }
+
+        private void QueueCommand(HyenaSqliteCommand command, object arg, object [] args)
+        {
+            if (warn_if_called_from_thread != null && Thread.CurrentThread == warn_if_called_from_thread) {
+                Hyena.Log.Warning ("HyenaSqliteConnection command issued from the main thread");
+            }
+
             while (true) {
                 lock (command_queue) {
                     if (transaction_thread == null || Thread.CurrentThread == transaction_thread) {
                         command_queue.Enqueue (command);
+                        args_queue.Enqueue (args);
+                        arg_queue.Enqueue (arg);
                         break;
                     }
                 }
@@ -401,11 +425,26 @@
             while (!dispose_requested) {
                 while (command_queue.Count > 0) {
                     HyenaSqliteCommand command;
+                    object [] args;
+                    object arg;
                     lock (command_queue) {
                         command = command_queue.Dequeue ();
+                        args = args_queue.Dequeue ();
+                        arg = arg_queue.Dequeue ();
+                    }
+
+                    // Ensure the command is not altered while applying values or executing
+                    lock (command) {
+                        command.WaitIfNotFinished ();
+
+                        if (arg != null) {
+                            command.ApplyValues (arg);
+                        } else if (args != null) {
+                            command.ApplyValues (args);
+                        }
+
+                        command.Execute (this, connection);
                     }
-                    
-                    command.Execute (this, connection);
 
                     lock (command_queue) {
                         results_ready++;

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	Mon Jan 26 04:24:37 2009
@@ -283,7 +283,7 @@
             lock (this) {
                 if (last_reload_fragment != model.ReloadFragment || last_reload_command == null) {
                     last_reload_fragment = model.ReloadFragment;
-                    last_reload_command = new HyenaSqliteCommand (String.Format ("BEGIN; {0}{1}; COMMIT", reload_sql, last_reload_fragment));
+                    last_reload_command = new HyenaSqliteCommand (String.Format ("{0}{1}", reload_sql, last_reload_fragment));
                 }
 
                 Clear ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]