[f-spot/mipmapped-loading] Standardize on one WorkerThreadTask.



commit 3b9a3b5f813ea27e97e1cc9dcc52e20f8ae3d037
Author: Ruben Vermeersch <ruben savanne be>
Date:   Sun Jun 20 17:29:31 2010 +0200

    Standardize on one WorkerThreadTask.

 src/Loaders/XdgThumbnailLoader.cs    |    9 ++-
 src/Makefile.am                      |    2 +-
 src/Tasks/QueuedTask.cs              |  115 ------------------------
 src/Tasks/Task.cs                    |    7 +-
 src/Tasks/Tests/TaskPriorityTests.cs |  126 ++++++++-------------------
 src/Tasks/Tests/TaskTests.cs         |    4 +-
 src/Tasks/WorkerThreadTask.cs        |  163 ++++++++++++++++++++++++++++++++++
 7 files changed, 212 insertions(+), 214 deletions(-)
---
diff --git a/src/Loaders/XdgThumbnailLoader.cs b/src/Loaders/XdgThumbnailLoader.cs
index bc08677..c24826d 100644
--- a/src/Loaders/XdgThumbnailLoader.cs
+++ b/src/Loaders/XdgThumbnailLoader.cs
@@ -20,10 +20,13 @@ namespace FSpot.Loaders
 
 		public Task<Pixbuf> FindBestPreview (int width, int height)
 		{
-			return new QueuedTask<Pixbuf> (() => {
+			return new WorkerThreadTask<Pixbuf> (() => {
 				var size = (width > 128 || height > 128) ? ThumbnailSize.Large : ThumbnailSize.Normal;
-				return XdgThumbnailSpec.LoadThumbnail (Loadable.Uri, size);
-			});
+				var pixbuf = XdgThumbnailSpec.LoadThumbnail (Loadable.Uri, size);
+				return pixbuf;
+			}) {
+				CancelWithChildren = true
+			};
 		}
 	}
 }
diff --git a/src/Makefile.am b/src/Makefile.am
index 5bea653..5278193 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -146,7 +146,7 @@ SOURCES = \
 	TagSelectionWidget.cs \
 	TagStore.cs \
 	Tasks/Task.cs \
-	Tasks/QueuedTask.cs \
+	Tasks/WorkerThreadTask.cs \
 	Tasks/Tests/TaskTests.cs \
 	Tasks/Tests/TaskPriorityTests.cs \
 	ThumbnailCache.cs \
diff --git a/src/Tasks/Task.cs b/src/Tasks/Task.cs
index efb2eae..d5cce2f 100644
--- a/src/Tasks/Task.cs
+++ b/src/Tasks/Task.cs
@@ -11,6 +11,7 @@ namespace FSpot.Tasks
 		void Execute ();
 
 		TaskPriority Priority { get; }
+		Task Parent { get; set; }
 	}
 
 	interface IChildrenHandling
@@ -44,8 +45,8 @@ namespace FSpot.Tasks
 	public abstract class Task<T> : Task, ISchedulable, IChildrenHandling
 	{
 		public bool CancelWithChildren { get; set; }
+		public Task Parent { get; set; }
 
-		private Task Parent { get; set; }
 		private List<Task> Children { get; set; }
 
 		private T result;
@@ -102,12 +103,12 @@ namespace FSpot.Tasks
 			WaitEvent.WaitOne ();
 		}
 
-		public void ContinueWith (Task<T> task)
+		public void ContinueWith (Task task)
 		{
 			ContinueWith (task, true);
 		}
 
-		public void ContinueWith (Task<T> task, bool autostart)
+		public void ContinueWith (Task task, bool autostart)
 		{
 			lock (Children) {
 				if (State == TaskState.Completed) {
diff --git a/src/Tasks/Tests/TaskPriorityTests.cs b/src/Tasks/Tests/TaskPriorityTests.cs
index 2e6adaa..620e052 100644
--- a/src/Tasks/Tests/TaskPriorityTests.cs
+++ b/src/Tasks/Tests/TaskPriorityTests.cs
@@ -14,50 +14,49 @@ namespace FSpot.Tasks.Tests
 	{
 		[SetUp]
 		public void Initialize () {
+			WorkerThreadTaskScheduler.Instance = null;
+			WorkerThreadTaskScheduler.Instance = new WorkerThreadTaskScheduler (false);
 			Hyena.Log.Debugging = true;
 		}
 
 		[Test]
 		public void TestDefaultPriority () {
-			var scheduler = new StaticScheduler ();
-			var task = new StaticTask (scheduler);
+			var task = new TestTask ();
 
 			// Task is initially unscheduled
 			Assert.AreEqual (TaskPriority.Normal, task.Priority);
 			Assert.AreEqual (TaskState.Pending, task.State);
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Sent to scheduler when started
 			task.Start ();
 			Assert.AreEqual (TaskPriority.Normal, task.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task.State);
-			Assert.AreEqual (new Task [] { task }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
 		[Test]
 		public void TestCancel () {
-			var scheduler = new StaticScheduler ();
-			var task = new StaticTask (scheduler);
+			var task = new TestTask ();
 
 			// Task is initially unscheduled
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Sent to scheduler when started
 			task.Start ();
 			Assert.AreEqual (TaskState.Scheduled, task.State);
-			Assert.AreEqual (new Task [] { task }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task }, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Removed from scheduler when cancelled
 			task.Cancel ();
 			Assert.AreEqual (TaskState.Cancelled, task.State);
-			Assert.AreEqual (new Task [] { }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
 		[Test]
 		public void TestOrdering () {
-			var scheduler = new StaticScheduler ();
-			var task1 = new StaticTask (scheduler);
-			var task2 = new StaticTask (scheduler) {
+			var task1 = new TestTask ();
+			var task2 = new TestTask () {
 				Priority = TaskPriority.Interactive
 			};
 
@@ -66,7 +65,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Pending, task1.State);
 			Assert.AreEqual (TaskPriority.Interactive, task2.Priority);
 			Assert.AreEqual (TaskState.Pending, task2.State);
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 			
 			// Sent to scheduler when started
 			task1.Start ();
@@ -74,7 +73,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskPriority.Interactive, task2.Priority);
 			Assert.AreEqual (TaskState.Pending, task2.State);
-			Assert.AreEqual (new Task [] { task1 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 			
 			// High priority task gets sent to the front of the queue
 			task2.Start ();
@@ -82,22 +81,21 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskPriority.Interactive, task2.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task2.State);
-			Assert.AreEqual (task2, scheduler.heap.Peek ());
-			Assert.AreEqual (new Task [] { task2, task1 }, scheduler.Tasks);
+			Assert.AreEqual (task2, WorkerThreadTaskScheduler.Instance.heap.Peek ());
+			Assert.AreEqual (new Task [] { task2, task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
 		[Test]
 		public void TestFIFOOrdering () {
-			var scheduler = new StaticScheduler ();
-			var task1 = new StaticTask (scheduler);
-			var task2 = new StaticTask (scheduler);
+			var task1 = new TestTask ();
+			var task2 = new TestTask ();
 
 			// Initially unscheduled
 			Assert.AreEqual (TaskPriority.Normal, task1.Priority);
 			Assert.AreEqual (TaskState.Pending, task1.State);
 			Assert.AreEqual (TaskPriority.Normal, task2.Priority);
 			Assert.AreEqual (TaskState.Pending, task2.State);
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 			
 			// Sent to scheduler when started
 			task1.Start ();
@@ -105,7 +103,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskPriority.Normal, task2.Priority);
 			Assert.AreEqual (TaskState.Pending, task2.State);
-			Assert.AreEqual (new Task [] { task1 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 			
 			// Equal priority tasks get scheduled FIFO
 			task2.Start ();
@@ -113,25 +111,24 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskPriority.Normal, task2.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task2.State);
-			Assert.AreEqual (task1, scheduler.heap.Peek ());
-			Assert.AreEqual (new Task [] { task1, task2 }, scheduler.Tasks);
+			Assert.AreEqual (task1, WorkerThreadTaskScheduler.Instance.heap.Peek ());
+			Assert.AreEqual (new Task [] { task1, task2 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
 		[Test]
 		public void TestPriorityInheritance () {
-			var scheduler = new StaticScheduler ();
-			var task1 = new StaticTask (scheduler);
-			var task2 = new StaticTask (scheduler);
-			var task3 = new StaticTask (scheduler) {
+			var task1 = new TestTask ();
+			var task2 = new TestTask ();
+			var task3 = new TestTask () {
 				Priority = TaskPriority.Interactive
 			};
 
 			// Initially unscheduled
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Send task1 to the scheduler
 			task1.Start ();
-			Assert.AreEqual (new Task [] { task1 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Start a continuation. Should cause task2 to be scheduled.
 			// It should inherit the priority from task3 and go to the
@@ -140,20 +137,19 @@ namespace FSpot.Tasks.Tests
 			task2.ContinueWith (task3);
 			Assert.AreEqual (TaskPriority.Normal, task1.Priority);
 			Assert.AreEqual (TaskPriority.Interactive, task2.Priority);
-			Assert.AreEqual (new Task [] { task2, task1 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task2, task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
 		[Test]
 		public void TestPriorityRevert () {
-			var scheduler = new StaticScheduler ();
-			var task1 = new StaticTask (scheduler);
-			var task2 = new StaticTask (scheduler);
-			var task3 = new StaticTask (scheduler) {
+			var task1 = new TestTask ();
+			var task2 = new TestTask ();
+			var task3 = new TestTask () {
 				Priority = TaskPriority.Interactive
 			};
 
 			// Initially unscheduled
-			Assert.AreEqual (new Task [] {}, scheduler.Tasks);
+			Assert.AreEqual (new Task [] {}, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Send task1 and task2 to the scheduler
 			task1.Start ();
@@ -162,7 +158,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskPriority.Normal, task2.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskState.Scheduled, task2.State);
-			Assert.AreEqual (new Task [] { task1, task2 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task1, task2 }, WorkerThreadTaskScheduler.Instance.Tasks);
 
 			// Start a continuation. Should cause task2 to be rescheduled.
 			// It should inherit the priority from task3 and go to the
@@ -172,7 +168,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskPriority.Interactive, task2.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskState.Scheduled, task2.State);
-			Assert.AreEqual (new Task [] { task2, task1 }, scheduler.Tasks);
+			Assert.AreEqual (new Task [] { task2, task1 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		
 			// Priority should revert after cancelling the child.
 			task3.Cancel ();
@@ -180,64 +176,14 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskPriority.Normal, task2.Priority);
 			Assert.AreEqual (TaskState.Scheduled, task1.State);
 			Assert.AreEqual (TaskState.Scheduled, task2.State);
-			Assert.AreEqual (new Task [] { task1, task2 }, scheduler.Tasks);
-		}
-	}
-
-	class StaticScheduler
-	{
-		internal IntervalHeap<Task> heap = new IntervalHeap<Task> ();	
-
-		public Task[] Tasks {
-			get {
-				List<Task> tasks = new List<Task> ();
-				foreach (var item in heap) {
-					tasks.Add (item);
-				}
-				return tasks.ToArray ();
-			}
-		}
-	}
-
-	class StaticTask : Task<bool>
-	{
-		public StaticScheduler Scheduler { get; set; }
-
-		internal StaticTask (StaticScheduler scheduler)
-		{
-			Scheduler = scheduler;
+			Assert.AreEqual (new Task [] { task1, task2 }, WorkerThreadTaskScheduler.Instance.Tasks);
 		}
 
-		protected override void InnerSchedule ()
-		{
-			lock (Scheduler.heap) {
-				Scheduler.heap.Push (this, (int) Priority);
-			}
-		}
-
-		protected override void InnerUnschedule ()
-		{
-			lock (Scheduler.heap) {
-				Scheduler.heap.Remove (this);
-			}
-		}
+		private class TestTask : WorkerThreadTask<bool> {
+			public TestTask () : base (() => true) {
 
-		protected override void InnerReschedule ()
-		{
-			lock (Scheduler.heap) {
-				Scheduler.heap.Remove (this);
-				Scheduler.heap.Push (this, (int) Priority);
 			}
 		}
-
-
-		protected override bool InnerExecute () {
-			throw new Exception ("Not supported for this task");
-		}
-
-		public override string ToString () {
-			return String.Format ("StaticTask (Priority: {0}, State: {1})", Priority, State);
-		}
 	}
 }
 #endif
diff --git a/src/Tasks/Tests/TaskTests.cs b/src/Tasks/Tests/TaskTests.cs
index 7ab05a8..f70947d 100644
--- a/src/Tasks/Tests/TaskTests.cs
+++ b/src/Tasks/Tests/TaskTests.cs
@@ -12,7 +12,7 @@ namespace FSpot.Tasks.Tests
 	{
 		[SetUp]
 		public void Initialize () {
-			QueueTaskScheduler.Instance = null;
+			WorkerThreadTaskScheduler.Instance = null;
 			Hyena.Log.Debugging = true;
 		}
 
@@ -96,7 +96,7 @@ namespace FSpot.Tasks.Tests
 			Assert.AreEqual (TaskState.Cancelled, t2.State);
 		}
 
-		class SimpleTask<T> : QueuedTask<T> {
+		class SimpleTask<T> : WorkerThreadTask<T> {
 			string label = null;
 
 			public SimpleTask (TaskHandler h) : base (h) {
diff --git a/src/Tasks/WorkerThreadTask.cs b/src/Tasks/WorkerThreadTask.cs
new file mode 100644
index 0000000..b29ff18
--- /dev/null
+++ b/src/Tasks/WorkerThreadTask.cs
@@ -0,0 +1,163 @@
+using Hyena;
+using Hyena.Collections;
+using System;
+using System.Threading;
+using System.Collections.Generic;
+
+namespace FSpot.Tasks
+{
+	/// <summary>
+	///    Simple task scheduler that executes all tasks on one worker thread.
+	/// </summary>
+	internal class WorkerThreadTaskScheduler {
+		static object sync_root = new object ();
+
+#region Singleton Pattern
+
+		static WorkerThreadTaskScheduler instance;
+		internal static WorkerThreadTaskScheduler Instance {
+			get {
+				lock (sync_root) {
+					if (instance == null) {
+						instance = new WorkerThreadTaskScheduler ();
+					}
+				}
+				return instance;
+			}
+			set {
+				if (value == null && instance != null) {
+					instance.Finish ();
+					instance = null;
+				}
+
+				if (instance != null)
+					throw new Exception ("Need to finish the current instance first!");
+				instance = value;
+			}
+		}
+
+#endregion
+
+#region Queue Management
+
+		internal IntervalHeap<Task> heap = new IntervalHeap<Task> ();	
+
+		internal void Schedule (Task task)
+		{
+			lock (heap) {
+				heap.Push (task, (int) task.Priority);
+				wait.Set ();
+			}
+		}
+
+		internal void Unschedule (Task task)
+		{
+			lock (heap) {
+				heap.Remove (task);
+			}
+		}
+
+		internal void Reschedule (Task task)
+		{
+			lock (heap) {
+				heap.Remove (task);
+				heap.Push (task, (int) task.Priority);
+				wait.Set ();
+			}
+		}
+
+		internal Task[] Tasks {
+			get {
+				List<Task> tasks = new List<Task> ();
+				foreach (var item in heap) {
+					tasks.Add (item);
+				}
+				return tasks.ToArray ();
+			}
+		}
+
+#endregion
+
+#region Worker Thread
+
+		internal WorkerThreadTaskScheduler () : this (true) {
+		}
+
+		internal WorkerThreadTaskScheduler (bool start_worker)
+		{
+			// Not starting the worker means that the scheduler won't work,
+			// but this can be useful for unit tests.
+			if (start_worker) {
+				worker = ThreadAssist.Spawn (SchedulerWorker);
+			}
+		}
+
+		EventWaitHandle wait = new AutoResetEvent (false);
+		Thread worker;
+		volatile bool should_halt = false;
+
+		void SchedulerWorker ()
+		{
+			while (!should_halt) {
+				Task task = null;
+				lock (heap) {
+					if (heap.Count > 0)
+						task = heap.Pop ();
+				}
+
+				if (task == null && !should_halt) {
+					wait.WaitOne ();
+					continue;
+				}
+
+				task.Execute ();
+			}
+		}
+
+		public void Finish ()
+		{
+			should_halt = true;
+			wait.Set ();
+			while (worker != null && !worker.Join (100))
+				wait.Set ();
+		}
+
+#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
+
+}



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