[f-spot/mipmapped-loading] Standardize on one WorkerThreadTask.
- From: Ruben Vermeersch <rubenv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [f-spot/mipmapped-loading] Standardize on one WorkerThreadTask.
- Date: Sun, 20 Jun 2010 15:33:32 +0000 (UTC)
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]