[f-spot/mipmapped-loading: 11/12] Simplify Task, add Threadpool support.



commit 02cb3c1832cd75399c21ac1200a63d84b6d388d5
Author: Ruben Vermeersch <ruben savanne be>
Date:   Fri Jul 2 15:53:30 2010 +0200

    Simplify Task, add Threadpool support.

 src/Import/ImportController.cs       |    4 +--
 src/Loaders/XdgThumbnailLoader.cs    |    2 +-
 src/Tasks/Task.cs                    |   40 ++++++++++++++-------
 src/Tasks/Tests/TaskPriorityTests.cs |    2 +-
 src/Tasks/Tests/TaskTests.cs         |    2 +-
 src/Tasks/WorkerThreadTask.cs        |   64 +++++++++++-----------------------
 src/ThumbnailCommand.cs              |    4 +--
 src/Widgets/Filmstrip.cs             |    6 +--
 src/Widgets/IconView.cs              |    6 +--
 9 files changed, 57 insertions(+), 73 deletions(-)
---
diff --git a/src/Import/ImportController.cs b/src/Import/ImportController.cs
index 471a6fb..b05ea3c 100644
--- a/src/Import/ImportController.cs
+++ b/src/Import/ImportController.cs
@@ -340,9 +340,7 @@ namespace FSpot.Import
         {
             var loader = App.Instance.Loaders.RequestLoader (photo.DefaultVersion);
             var preview_task = loader.FindBestPreview (256, 256);
-            var task = new WorkerThreadTask<bool> (() => { preview_task.Result.Dispose (); return false; }) {
-                Priority = TaskPriority.Background
-            };
+            var task = new Task<bool> (() => { preview_task.Result.Dispose (); return false; }, TaskPriority.Background);
             preview_task.ContinueWith (task);
         }
 
diff --git a/src/Loaders/XdgThumbnailLoader.cs b/src/Loaders/XdgThumbnailLoader.cs
index eceb75c..9f92e15 100644
--- a/src/Loaders/XdgThumbnailLoader.cs
+++ b/src/Loaders/XdgThumbnailLoader.cs
@@ -20,7 +20,7 @@ namespace FSpot.Loaders
 
 		public Task<Pixbuf> FindBestPreview (int width, int height)
 		{
-			return new WorkerThreadTask<Pixbuf> (() => {
+			return new Task<Pixbuf> (() => {
 				var size = (width > 128 || height > 128) ? ThumbnailSize.Large : ThumbnailSize.Normal;
 				var pixbuf = XdgThumbnailSpec.LoadThumbnail (Loadable.Uri, size);
 				return pixbuf;
diff --git a/src/Tasks/Task.cs b/src/Tasks/Task.cs
index d5cce2f..6c390e9 100644
--- a/src/Tasks/Task.cs
+++ b/src/Tasks/Task.cs
@@ -26,6 +26,13 @@ namespace FSpot.Tasks
 		void Reschedule ();
 	}
 
+	public interface IScheduler
+	{
+		void Schedule (Task task);
+		void Unschedule (Task task);
+		void Reschedule (Task task);
+	}
+
 	public enum TaskState
 	{
 		Pending,
@@ -42,10 +49,18 @@ namespace FSpot.Tasks
 		Interactive
 	}
 
-	public abstract class Task<T> : Task, ISchedulable, IChildrenHandling
+	public class Task<T> : Task, ISchedulable, IChildrenHandling
 	{
+		static IScheduler DefaultScheduler {
+			get { return WorkerThreadTaskScheduler.Instance; }
+		}
+
+		public delegate T TaskHandler ();
+
 		public bool CancelWithChildren { get; set; }
 		public Task Parent { get; set; }
+		public IScheduler Scheduler { get; set; }
+		public TaskHandler Handler { get; set; }
 
 		private List<Task> Children { get; set; }
 
@@ -80,13 +95,18 @@ namespace FSpot.Tasks
 
 		private EventWaitHandle WaitEvent { get; set; }
 
-		public Task ()
+		public Task (TaskHandler handler) : this (handler, TaskPriority.Normal) {
+		}
+
+		public Task (TaskHandler handler, TaskPriority priority)
 		{
 			CancelWithChildren = false;
 			Children = new List<Task> ();
 			WaitEvent = new ManualResetEvent (false);
 			State = TaskState.Pending;
-			Priority = TaskPriority.Normal;
+			Priority = priority;
+			Scheduler = DefaultScheduler;
+			Handler = handler;
 		}
 
 		public void Start ()
@@ -175,7 +195,7 @@ namespace FSpot.Tasks
 				return;
 
 			try {
-				result = InnerExecute ();
+				result = Handler ();
 				State = TaskState.Completed;
 
 				foreach (var child in Children) {
@@ -189,8 +209,6 @@ namespace FSpot.Tasks
 			}
 		}
 
-		protected abstract T InnerExecute ();
-
 #region Scheduling
 
 		void ISchedulable.Schedule ()
@@ -200,25 +218,21 @@ namespace FSpot.Tasks
 			if (State != TaskState.Pending)
 				throw new Exception ("Can only schedule pending tasks!");
 			State = TaskState.Scheduled;
-			InnerSchedule ();
+			Scheduler.Schedule (this);
 		}
 
 		void ISchedulable.Unschedule ()
 		{
 			if (State == TaskState.Scheduled)
 				State = TaskState.Pending;
-			InnerUnschedule ();
+			Scheduler.Unschedule (this);
 		}
 
 		void ISchedulable.Reschedule ()
 		{
-			InnerReschedule ();
+			Scheduler.Reschedule (this);
 		}
 
-		protected abstract void InnerSchedule ();
-		protected abstract void InnerUnschedule ();
-		protected abstract void InnerReschedule ();
-
 #endregion
 
 	}
diff --git a/src/Tasks/Tests/TaskPriorityTests.cs b/src/Tasks/Tests/TaskPriorityTests.cs
index 16e4415..38acbd0 100644
--- a/src/Tasks/Tests/TaskPriorityTests.cs
+++ b/src/Tasks/Tests/TaskPriorityTests.cs
@@ -179,7 +179,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (new Task [] { task1, task2 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
-		private class TestTask : WorkerThreadTask<bool> {
+		private class TestTask : Task<bool> {
 			public TestTask () : base (() => true) {
 
 			}
diff --git a/src/Tasks/Tests/TaskTests.cs b/src/Tasks/Tests/TaskTests.cs
index f70947d..6b74fcd 100644
--- a/src/Tasks/Tests/TaskTests.cs
+++ b/src/Tasks/Tests/TaskTests.cs
@@ -96,7 +96,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Cancelled, t2.State);
 		}
 
-		class SimpleTask<T> : WorkerThreadTask<T> {
+		class SimpleTask<T> : Task<T> {
 			string label = null;
 
 			public SimpleTask (TaskHandler h) : base (h) {
diff --git a/src/Tasks/WorkerThreadTask.cs b/src/Tasks/WorkerThreadTask.cs
index 7829b16..bb22922 100644
--- a/src/Tasks/WorkerThreadTask.cs
+++ b/src/Tasks/WorkerThreadTask.cs
@@ -7,9 +7,9 @@ using System.Collections.Generic;
 namespace FSpot.Tasks
 {
 	/// <summary>
-	///    Simple task scheduler that executes all tasks on one worker thread.
+	///    Simple task scheduler that pumps the threadpool.
 	/// </summary>
-	internal class WorkerThreadTaskScheduler {
+	internal class WorkerThreadTaskScheduler : IScheduler {
 		static object sync_root = new object ();
 
 #region Singleton Pattern
@@ -42,7 +42,7 @@ namespace FSpot.Tasks
 
 		internal IntervalHeap<Task> heap = new IntervalHeap<Task> ();
 
-		internal void Schedule (Task task)
+		public void Schedule (Task task)
 		{
 			lock (heap) {
 				heap.Push (task, (int) task.Priority);
@@ -50,14 +50,14 @@ namespace FSpot.Tasks
 			}
 		}
 
-		internal void Unschedule (Task task)
+		public void Unschedule (Task task)
 		{
 			lock (heap) {
 				heap.Remove (task);
 			}
 		}
 
-		internal void Reschedule (Task task)
+		public void Reschedule (Task task)
 		{
 			lock (heap) {
 				heap.Remove (task);
@@ -85,6 +85,9 @@ namespace FSpot.Tasks
 
 		internal WorkerThreadTaskScheduler (bool start_worker)
 		{
+			max_tasks = Environment.ProcessorCount * 2;
+			Log.DebugFormat ("Doing at most {0} tasks", max_tasks);
+
 			// Not starting the worker means that the scheduler won't work,
 			// but this can be useful for unit tests.
 			if (start_worker) {
@@ -94,7 +97,10 @@ namespace FSpot.Tasks
 
 		EventWaitHandle wait = new AutoResetEvent (false);
 		Thread worker;
+
 		volatile bool should_halt = false;
+		int max_tasks = 0;
+		volatile int tasks_queued = 0;
 
 		void SchedulerWorker ()
 		{
@@ -105,12 +111,19 @@ namespace FSpot.Tasks
 						task = heap.Pop ();
 				}
 
-				if (task == null && !should_halt) {
+				if ((tasks_queued >= max_tasks || task == null) && !should_halt) {
 					wait.WaitOne ();
-					continue;
+					if (task == null)
+						continue;
 				}
 
-				task.Execute ();
+				Interlocked.Increment (ref tasks_queued);
+				ThreadPool.QueueUserWorkItem ((o) => {
+					task.Execute ();
+					Log.DebugFormat ("Finished task {0}", task);
+					Interlocked.Decrement (ref tasks_queued);
+					wait.Set ();
+				});
 			}
 		}
 
@@ -125,39 +138,4 @@ namespace FSpot.Tasks
 #endregion
 
 	}
-
-#region Task
-
-	public class WorkerThreadTask<T> : Task<T>
-	{
-		public delegate T TaskHandler ();
-
-		TaskHandler handler;
-
-		public WorkerThreadTask (TaskHandler h) {
-			this.handler = h;
-		}
-
-		protected override void InnerSchedule ()
-		{
-			WorkerThreadTaskScheduler.Instance.Schedule (this);
-		}
-
-		protected override void InnerUnschedule ()
-		{
-			WorkerThreadTaskScheduler.Instance.Unschedule (this);
-		}
-
-		protected override void InnerReschedule ()
-		{
-			WorkerThreadTaskScheduler.Instance.Reschedule (this);
-		}
-
-		protected override T InnerExecute () {
-			return handler ();
-		}
-	}
-
-#endregion
-
 }
diff --git a/src/ThumbnailCommand.cs b/src/ThumbnailCommand.cs
index 95f0bfc..7e3d14e 100644
--- a/src/ThumbnailCommand.cs
+++ b/src/ThumbnailCommand.cs
@@ -46,9 +46,7 @@ public class ThumbnailCommand {
     {
         var loader = App.Instance.Loaders.RequestLoader (item);
         var preview_task = loader.FindBestPreview (256, 256);
-        var task = new WorkerThreadTask<bool> (() => { preview_task.Result.Dispose (); return false; }) {
-            Priority = TaskPriority.Background
-        };
+        var task = new Task<bool> (() => { preview_task.Result.Dispose (); return false; }, TaskPriority.Background);
         preview_task.ContinueWith (task);
     }
 }
diff --git a/src/Widgets/Filmstrip.cs b/src/Widgets/Filmstrip.cs
index 97ec661..55c69df 100644
--- a/src/Widgets/Filmstrip.cs
+++ b/src/Widgets/Filmstrip.cs
@@ -608,7 +608,7 @@ namespace FSpot.Widgets
 
 				var loader = App.Instance.Loaders.RequestLoader ((selection.Collection [i]).DefaultVersion);
 				var preview_task = loader.FindBestPreview (ThumbSize, ThumbSize);
-				var task = new WorkerThreadTask<bool> (() => {
+				var task = new Task<bool> (() => {
 					Pixbuf pixbuf = preview_task.Result;
 					if (SquaredThumbs) {
 						current = PixbufUtils.IconFromPixbuf (pixbuf, ThumbSize);
@@ -622,9 +622,7 @@ namespace FSpot.Widgets
 						QueueDraw ();
 						});
 					return false;
-				}) {
-					Priority = TaskPriority.Interactive
-				};
+				}, TaskPriority.Interactive);
 				preview_task.ContinueWith (task);
 			} else {
 				current = thumb_cache.Get (uri).ShallowCopy ();
diff --git a/src/Widgets/IconView.cs b/src/Widgets/IconView.cs
index c590d3b..ada64a9 100644
--- a/src/Widgets/IconView.cs
+++ b/src/Widgets/IconView.cs
@@ -845,7 +845,7 @@ namespace FSpot.Widgets
 		{
 			var loader = App.Instance.Loaders.RequestLoader (item.DefaultVersion);
 			var preview_task = loader.FindBestPreview (ThumbnailWidth, ThumbnailHeight);
-			var task = new WorkerThreadTask<bool> (() => {
+			var task = new Task<bool> (() => {
 				if (preview_task.Result == null)
 					return false;
 				ThreadAssist.ProxyToMain (() => {
@@ -854,9 +854,7 @@ namespace FSpot.Widgets
 					}
 				});
 				return false;
-			}) {
-				Priority = TaskPriority.Interactive
-			};
+			}, TaskPriority.Interactive);
 			preview_task.ContinueWith (task);
 		}
 



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