22 changed files with 744 additions and 679 deletions
@ -1,637 +0,0 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="Dispatcher.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Windows.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Security; |
|||
using System.Threading; |
|||
using Perspex.Windows.Interop; |
|||
|
|||
public enum DispatcherPriority |
|||
{ |
|||
Invalid = -1, |
|||
Inactive = 0, |
|||
SystemIdle = 1, |
|||
ApplicationIdle = 2, |
|||
ContextIdle = 3, |
|||
Background = 4, |
|||
Input = 5, |
|||
Loaded = 6, |
|||
Render = 7, |
|||
DataBind = 8, |
|||
Normal = 9, |
|||
Send = 10, |
|||
} |
|||
|
|||
[Flags] |
|||
internal enum Flags |
|||
{ |
|||
ShutdownStarted = 1, |
|||
Shutdown = 2, |
|||
Disabled = 4 |
|||
} |
|||
|
|||
public sealed class Dispatcher |
|||
{ |
|||
private const int TopPriority = (int)DispatcherPriority.Send; |
|||
|
|||
private static Dictionary<Thread, Dispatcher> dispatchers = new Dictionary<Thread, Dispatcher>(); |
|||
|
|||
private static object olock = new object(); |
|||
|
|||
private static DispatcherFrame mainExecutionFrame = new DispatcherFrame(); |
|||
|
|||
private Thread baseThread; |
|||
|
|||
private PokableQueue[] priorityQueues = new PokableQueue[TopPriority + 1]; |
|||
|
|||
private Flags flags; |
|||
|
|||
private int queueBits; |
|||
|
|||
private DispatcherFrame currentFrame; |
|||
|
|||
private Dispatcher(Thread t) |
|||
{ |
|||
this.baseThread = t; |
|||
|
|||
for (int i = 1; i <= (int)DispatcherPriority.Send; i++) |
|||
{ |
|||
this.priorityQueues[i] = new PokableQueue(); |
|||
} |
|||
} |
|||
|
|||
public event EventHandler ShutdownStarted; |
|||
|
|||
public event EventHandler ShutdownFinished; |
|||
|
|||
public static Dispatcher CurrentDispatcher |
|||
{ |
|||
get |
|||
{ |
|||
lock (olock) |
|||
{ |
|||
Thread t = Thread.CurrentThread; |
|||
Dispatcher dis = FromThread(t); |
|||
|
|||
if (dis != null) |
|||
{ |
|||
return dis; |
|||
} |
|||
|
|||
dis = new Dispatcher(t); |
|||
dispatchers[t] = dis; |
|||
return dis; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Thread Thread |
|||
{ |
|||
get |
|||
{ |
|||
return this.baseThread; |
|||
} |
|||
} |
|||
|
|||
public bool HasShutdownStarted |
|||
{ |
|||
get |
|||
{ |
|||
return (this.flags & Flags.ShutdownStarted) != 0; |
|||
} |
|||
} |
|||
|
|||
public bool HasShutdownFinished |
|||
{ |
|||
get |
|||
{ |
|||
return (this.flags & Flags.Shutdown) != 0; |
|||
} |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public static void ExitAllFrames() |
|||
{ |
|||
Dispatcher dis = CurrentDispatcher; |
|||
|
|||
for (DispatcherFrame frame = dis.currentFrame; frame != null; frame = frame.ParentFrame) |
|||
{ |
|||
if (frame.ExitOnRequest) |
|||
{ |
|||
frame.Continue = false; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static Dispatcher FromThread(Thread thread) |
|||
{ |
|||
Dispatcher dis; |
|||
|
|||
if (dispatchers.TryGetValue(thread, out dis)) |
|||
{ |
|||
return dis; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public static void PushFrame(DispatcherFrame frame) |
|||
{ |
|||
if (frame == null) |
|||
{ |
|||
throw new ArgumentNullException("frame"); |
|||
} |
|||
|
|||
Dispatcher dis = CurrentDispatcher; |
|||
|
|||
if (dis.HasShutdownFinished) |
|||
{ |
|||
throw new InvalidOperationException("The Dispatcher has shut down"); |
|||
} |
|||
|
|||
if (frame.Running != null) |
|||
{ |
|||
throw new InvalidOperationException("Frame is already running on a different dispatcher"); |
|||
} |
|||
|
|||
if ((dis.flags & Flags.Disabled) != 0) |
|||
{ |
|||
throw new InvalidOperationException("Dispatcher processing has been disabled"); |
|||
} |
|||
|
|||
frame.ParentFrame = dis.currentFrame; |
|||
dis.currentFrame = frame; |
|||
|
|||
frame.Running = dis; |
|||
|
|||
dis.RunFrame(frame); |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public static void Run() |
|||
{ |
|||
PushFrame(mainExecutionFrame); |
|||
} |
|||
|
|||
public static void ValidatePriority(DispatcherPriority priority, string parameterName) |
|||
{ |
|||
if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException(parameterName); |
|||
} |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool CheckAccess() |
|||
{ |
|||
return Thread.CurrentThread == this.baseThread; |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public void VerifyAccess() |
|||
{ |
|||
if (Thread.CurrentThread != this.baseThread) |
|||
{ |
|||
throw new InvalidOperationException("Invoked from a different thread"); |
|||
} |
|||
} |
|||
|
|||
public DispatcherOperation BeginInvoke(Delegate method, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
DispatcherOperation op = new DispatcherOperation(this, priority, method); |
|||
this.Queue(priority, op); |
|||
|
|||
return op; |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
DispatcherOperation op = new DispatcherOperation(this, priority, method, arg); |
|||
|
|||
this.Queue(priority, op); |
|||
|
|||
return op; |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg, params object[] args) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
DispatcherOperation op = new DispatcherOperation(this, priority, method, arg, args); |
|||
this.Queue(priority, op); |
|||
|
|||
return op; |
|||
} |
|||
|
|||
public object Invoke(Delegate method, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public object Invoke(Delegate method, TimeSpan timeout, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public object Invoke(Delegate method, TimeSpan timeout, DispatcherPriority priority, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public object Invoke(Delegate method, DispatcherPriority priority, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, Delegate method) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
DispatcherOperation op = new DispatcherOperation(this, priority, method); |
|||
this.Queue(priority, op); |
|||
PushFrame(new DispatcherFrame()); |
|||
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, Delegate method, object arg) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
this.Queue(priority, new DispatcherOperation(this, priority, method, arg)); |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, Delegate method, object arg, params object[] args) |
|||
{ |
|||
if (priority < 0 || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
this.Queue(priority, new DispatcherOperation(this, priority, method, arg, args)); |
|||
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Browsable(false)] |
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg, params object[] args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public void InvokeShutdown() |
|||
{ |
|||
this.flags |= Flags.ShutdownStarted; |
|||
|
|||
UnmanagedMethods.PostMessage( |
|||
IntPtr.Zero, |
|||
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM, |
|||
IntPtr.Zero, |
|||
IntPtr.Zero); |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public void BeginInvokeShutdown(DispatcherPriority priority) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
internal void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority) |
|||
{ |
|||
int oldp = (int)oldpriority; |
|||
PokableQueue q = this.priorityQueues[oldp]; |
|||
|
|||
lock (q) |
|||
{ |
|||
q.Remove(op); |
|||
} |
|||
|
|||
this.Queue(op.Priority, op); |
|||
} |
|||
|
|||
private void Queue(DispatcherPriority priority, DispatcherOperation x) |
|||
{ |
|||
int p = (int)priority; |
|||
PokableQueue q = this.priorityQueues[p]; |
|||
|
|||
lock (q) |
|||
{ |
|||
int flag = 1 << p; |
|||
q.Enqueue(x); |
|||
this.queueBits |= flag; |
|||
} |
|||
|
|||
if (Thread.CurrentThread != this.baseThread) |
|||
{ |
|||
UnmanagedMethods.PostMessage( |
|||
IntPtr.Zero, |
|||
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM, |
|||
IntPtr.Zero, |
|||
IntPtr.Zero); |
|||
} |
|||
} |
|||
|
|||
private void PerformShutdown() |
|||
{ |
|||
EventHandler h; |
|||
|
|||
h = this.ShutdownStarted; |
|||
if (h != null) |
|||
{ |
|||
h(this, new EventArgs()); |
|||
} |
|||
|
|||
this.flags |= Flags.Shutdown; |
|||
|
|||
h = this.ShutdownFinished; |
|||
if (h != null) |
|||
{ |
|||
h(this, new EventArgs()); |
|||
} |
|||
|
|||
this.priorityQueues = null; |
|||
} |
|||
|
|||
private void RunFrame(DispatcherFrame frame) |
|||
{ |
|||
do |
|||
{ |
|||
while (this.queueBits != 0) |
|||
{ |
|||
for (int i = TopPriority; i > 0 && this.queueBits != 0; i--) |
|||
{ |
|||
int currentBit = this.queueBits & (1 << i); |
|||
if (currentBit != 0) |
|||
{ |
|||
PokableQueue q = this.priorityQueues[i]; |
|||
|
|||
do |
|||
{ |
|||
DispatcherOperation task; |
|||
|
|||
lock (q) |
|||
{ |
|||
task = (DispatcherOperation)q.Dequeue(); |
|||
} |
|||
|
|||
task.Invoke(); |
|||
|
|||
if (!frame.Continue) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (this.HasShutdownStarted) |
|||
{ |
|||
this.PerformShutdown(); |
|||
return; |
|||
} |
|||
|
|||
lock (q) |
|||
{ |
|||
if (q.Count == 0) |
|||
{ |
|||
this.queueBits &= ~(1 << i); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (currentBit < (this.queueBits & ~currentBit)) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
while (true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
UnmanagedMethods.MSG msg; |
|||
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0); |
|||
UnmanagedMethods.TranslateMessage(ref msg); |
|||
UnmanagedMethods.DispatchMessage(ref msg); |
|||
|
|||
if (this.HasShutdownStarted) |
|||
{ |
|||
this.PerformShutdown(); |
|||
return; |
|||
} |
|||
} |
|||
while (frame.Continue); |
|||
} |
|||
|
|||
private class PokableQueue |
|||
{ |
|||
private const int InitialCapacity = 32; |
|||
|
|||
private int size, head, tail; |
|||
private object[] array; |
|||
|
|||
internal PokableQueue(int capacity) |
|||
{ |
|||
this.array = new object[capacity]; |
|||
} |
|||
|
|||
internal PokableQueue() |
|||
: this(InitialCapacity) |
|||
{ |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
return this.size; |
|||
} |
|||
} |
|||
|
|||
public void Enqueue(object obj) |
|||
{ |
|||
if (this.size == this.array.Length) |
|||
{ |
|||
this.Grow(); |
|||
} |
|||
|
|||
this.array[this.tail] = obj; |
|||
this.tail = (this.tail + 1) % this.array.Length; |
|||
this.size++; |
|||
} |
|||
|
|||
public object Dequeue() |
|||
{ |
|||
if (this.size < 1) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
object result = this.array[this.head]; |
|||
this.array[this.head] = null; |
|||
this.head = (this.head + 1) % this.array.Length; |
|||
this.size--; |
|||
return result; |
|||
} |
|||
|
|||
public void Remove(object obj) |
|||
{ |
|||
for (int i = 0; i < this.size; i++) |
|||
{ |
|||
if (this.array[(this.head + i) % this.array.Length] == obj) |
|||
{ |
|||
for (int j = i; j < this.size - i; j++) |
|||
{ |
|||
this.array[(this.head + j) % this.array.Length] = this.array[(this.head + j + 1) % this.array.Length]; |
|||
} |
|||
|
|||
this.size--; |
|||
if (this.size < 0) |
|||
{ |
|||
this.size = this.array.Length - 1; |
|||
} |
|||
|
|||
this.tail--; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void Grow() |
|||
{ |
|||
int newc = this.array.Length * 2; |
|||
object[] newContents = new object[newc]; |
|||
this.array.CopyTo(newContents, 0); |
|||
this.array = newContents; |
|||
this.head = 0; |
|||
this.tail = this.head + this.size; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,371 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="Dispatcher.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Windows.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Security; |
|||
using System.Threading; |
|||
using Perspex.Threading; |
|||
using Perspex.Windows.Interop; |
|||
|
|||
[Flags] |
|||
internal enum Flags |
|||
{ |
|||
ShutdownStarted = 1, |
|||
Shutdown = 2, |
|||
Disabled = 4 |
|||
} |
|||
|
|||
public sealed class WindowsDispatcher : Dispatcher |
|||
{ |
|||
private const int TopPriority = (int)DispatcherPriority.Send; |
|||
|
|||
private static Dictionary<Thread, WindowsDispatcher> dispatchers = new Dictionary<Thread, WindowsDispatcher>(); |
|||
|
|||
private static object olock = new object(); |
|||
|
|||
private Thread baseThread; |
|||
|
|||
private PokableQueue[] priorityQueues = new PokableQueue[TopPriority + 1]; |
|||
|
|||
private Flags flags; |
|||
|
|||
private int queueBits; |
|||
|
|||
private WindowsDispatcher(Thread t) |
|||
{ |
|||
this.baseThread = t; |
|||
|
|||
for (int i = 1; i <= (int)DispatcherPriority.Send; i++) |
|||
{ |
|||
this.priorityQueues[i] = new PokableQueue(); |
|||
} |
|||
} |
|||
|
|||
public event EventHandler ShutdownStarted; |
|||
|
|||
public event EventHandler ShutdownFinished; |
|||
|
|||
public override DispatcherFrame CurrentFrame |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public Thread Thread |
|||
{ |
|||
get { return this.baseThread; } |
|||
} |
|||
|
|||
public bool HasShutdownStarted |
|||
{ |
|||
get { return (this.flags & Flags.ShutdownStarted) != 0; } |
|||
} |
|||
|
|||
public override bool HasShutdownFinished |
|||
{ |
|||
get { return (this.flags & Flags.Shutdown) != 0; } |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public static void ExitAllFrames() |
|||
{ |
|||
Dispatcher dis = CurrentDispatcher; |
|||
|
|||
for (DispatcherFrame frame = dis.CurrentFrame; frame != null; frame = frame.ParentFrame) |
|||
{ |
|||
if (frame.ExitOnRequest) |
|||
{ |
|||
frame.Continue = false; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static WindowsDispatcher FromThread(Thread thread) |
|||
{ |
|||
WindowsDispatcher dis; |
|||
|
|||
if (dispatchers.TryGetValue(thread, out dis)) |
|||
{ |
|||
return dis; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public static WindowsDispatcher GetThreadDispatcher() |
|||
{ |
|||
lock (olock) |
|||
{ |
|||
Thread t = Thread.CurrentThread; |
|||
WindowsDispatcher dis = FromThread(t); |
|||
|
|||
if (dis != null) |
|||
{ |
|||
return dis; |
|||
} |
|||
|
|||
dis = new WindowsDispatcher(t); |
|||
dispatchers[t] = dis; |
|||
return dis; |
|||
} |
|||
} |
|||
|
|||
public override DispatcherOperation BeginInvoke(Action method) |
|||
{ |
|||
return this.BeginInvoke(DispatcherPriority.Normal, method); |
|||
} |
|||
|
|||
public override DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method) |
|||
{ |
|||
if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send) |
|||
{ |
|||
throw new InvalidEnumArgumentException("priority"); |
|||
} |
|||
|
|||
if (priority == DispatcherPriority.Inactive) |
|||
{ |
|||
throw new ArgumentException("priority can not be inactive", "priority"); |
|||
} |
|||
|
|||
if (method == null) |
|||
{ |
|||
throw new ArgumentNullException("method"); |
|||
} |
|||
|
|||
DispatcherOperation op = new DispatcherOperation(this, priority, method); |
|||
this.Queue(priority, op); |
|||
return op; |
|||
} |
|||
|
|||
[SecurityCritical] |
|||
public void InvokeShutdown() |
|||
{ |
|||
this.flags |= Flags.ShutdownStarted; |
|||
|
|||
UnmanagedMethods.PostMessage( |
|||
IntPtr.Zero, |
|||
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM, |
|||
IntPtr.Zero, |
|||
IntPtr.Zero); |
|||
} |
|||
|
|||
protected override void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority) |
|||
{ |
|||
int oldp = (int)oldpriority; |
|||
PokableQueue q = this.priorityQueues[oldp]; |
|||
|
|||
lock (q) |
|||
{ |
|||
q.Remove(op); |
|||
} |
|||
|
|||
this.Queue(op.Priority, op); |
|||
} |
|||
|
|||
private void Queue(DispatcherPriority priority, DispatcherOperation x) |
|||
{ |
|||
int p = (int)priority; |
|||
PokableQueue q = this.priorityQueues[p]; |
|||
|
|||
lock (q) |
|||
{ |
|||
int flag = 1 << p; |
|||
q.Enqueue(x); |
|||
this.queueBits |= flag; |
|||
} |
|||
|
|||
if (Thread.CurrentThread != this.baseThread) |
|||
{ |
|||
UnmanagedMethods.PostMessage( |
|||
IntPtr.Zero, |
|||
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM, |
|||
IntPtr.Zero, |
|||
IntPtr.Zero); |
|||
} |
|||
} |
|||
|
|||
private void PerformShutdown() |
|||
{ |
|||
EventHandler h; |
|||
|
|||
h = this.ShutdownStarted; |
|||
if (h != null) |
|||
{ |
|||
h(this, new EventArgs()); |
|||
} |
|||
|
|||
this.flags |= Flags.Shutdown; |
|||
|
|||
h = this.ShutdownFinished; |
|||
if (h != null) |
|||
{ |
|||
h(this, new EventArgs()); |
|||
} |
|||
|
|||
this.priorityQueues = null; |
|||
} |
|||
|
|||
protected override void RunFrame(DispatcherFrame frame) |
|||
{ |
|||
do |
|||
{ |
|||
while (this.queueBits != 0) |
|||
{ |
|||
for (int i = TopPriority; i > 0 && this.queueBits != 0; i--) |
|||
{ |
|||
int currentBit = this.queueBits & (1 << i); |
|||
if (currentBit != 0) |
|||
{ |
|||
PokableQueue q = this.priorityQueues[i]; |
|||
|
|||
do |
|||
{ |
|||
DispatcherOperation task; |
|||
|
|||
lock (q) |
|||
{ |
|||
task = (DispatcherOperation)q.Dequeue(); |
|||
} |
|||
|
|||
task.Invoke(); |
|||
|
|||
if (!frame.Continue) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (this.HasShutdownStarted) |
|||
{ |
|||
this.PerformShutdown(); |
|||
return; |
|||
} |
|||
|
|||
lock (q) |
|||
{ |
|||
if (q.Count == 0) |
|||
{ |
|||
this.queueBits &= ~(1 << i); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (currentBit < (this.queueBits & ~currentBit)) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
while (true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
UnmanagedMethods.MSG msg; |
|||
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0); |
|||
UnmanagedMethods.TranslateMessage(ref msg); |
|||
UnmanagedMethods.DispatchMessage(ref msg); |
|||
|
|||
if (this.HasShutdownStarted) |
|||
{ |
|||
this.PerformShutdown(); |
|||
return; |
|||
} |
|||
} |
|||
while (frame.Continue); |
|||
} |
|||
|
|||
private class PokableQueue |
|||
{ |
|||
private const int InitialCapacity = 32; |
|||
|
|||
private int size, head, tail; |
|||
private object[] array; |
|||
|
|||
internal PokableQueue(int capacity) |
|||
{ |
|||
this.array = new object[capacity]; |
|||
} |
|||
|
|||
internal PokableQueue() |
|||
: this(InitialCapacity) |
|||
{ |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
return this.size; |
|||
} |
|||
} |
|||
|
|||
public void Enqueue(object obj) |
|||
{ |
|||
if (this.size == this.array.Length) |
|||
{ |
|||
this.Grow(); |
|||
} |
|||
|
|||
this.array[this.tail] = obj; |
|||
this.tail = (this.tail + 1) % this.array.Length; |
|||
this.size++; |
|||
} |
|||
|
|||
public object Dequeue() |
|||
{ |
|||
if (this.size < 1) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
object result = this.array[this.head]; |
|||
this.array[this.head] = null; |
|||
this.head = (this.head + 1) % this.array.Length; |
|||
this.size--; |
|||
return result; |
|||
} |
|||
|
|||
public void Remove(object obj) |
|||
{ |
|||
for (int i = 0; i < this.size; i++) |
|||
{ |
|||
if (this.array[(this.head + i) % this.array.Length] == obj) |
|||
{ |
|||
for (int j = i; j < this.size - i; j++) |
|||
{ |
|||
this.array[(this.head + j) % this.array.Length] = this.array[(this.head + j + 1) % this.array.Length]; |
|||
} |
|||
|
|||
this.size--; |
|||
if (this.size < 0) |
|||
{ |
|||
this.size = this.array.Length - 1; |
|||
} |
|||
|
|||
this.tail--; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void Grow() |
|||
{ |
|||
int newc = this.array.Length * 2; |
|||
object[] newContents = new object[newc]; |
|||
this.array.CopyTo(newContents, 0); |
|||
this.array = newContents; |
|||
this.head = 0; |
|||
this.tail = this.head + this.size; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="WindowsPlatform.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Windows |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Perspex.Platform; |
|||
using Perspex.Threading; |
|||
using Perspex.Windows.Interop; |
|||
using Perspex.Windows.Threading; |
|||
using Splat; |
|||
|
|||
public class WindowsPlatform : IPlatformThreadingInterface |
|||
{ |
|||
private static WindowsPlatform instance = new WindowsPlatform(); |
|||
|
|||
private Dictionary<IntPtr, UnmanagedMethods.TimerProc> timerCallbacks = |
|||
new Dictionary<IntPtr, UnmanagedMethods.TimerProc>(); |
|||
|
|||
public static void Initialize() |
|||
{ |
|||
var locator = Locator.CurrentMutable; |
|||
locator.Register(() => instance, typeof(IPlatformThreadingInterface)); |
|||
} |
|||
|
|||
public Dispatcher GetThreadDispatcher() |
|||
{ |
|||
return WindowsDispatcher.GetThreadDispatcher(); |
|||
} |
|||
|
|||
|
|||
public void KillTimer(object handle) |
|||
{ |
|||
this.timerCallbacks.Remove((IntPtr)handle); |
|||
UnmanagedMethods.KillTimer(IntPtr.Zero, (IntPtr)handle); |
|||
} |
|||
|
|||
public object StartTimer(TimeSpan interval, Action callback) |
|||
{ |
|||
UnmanagedMethods.TimerProc timerDelegate = (UnmanagedMethods.TimerProc) |
|||
((hWnd, uMsg, nIDEvent, dwTime) => callback()); |
|||
|
|||
IntPtr handle = UnmanagedMethods.SetTimer( |
|||
IntPtr.Zero, |
|||
IntPtr.Zero, |
|||
(uint)interval.TotalMilliseconds, |
|||
timerDelegate); |
|||
|
|||
this.timerCallbacks.Add(handle, timerDelegate); |
|||
|
|||
return handle; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="IPlatformThreadingInterface.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Platform |
|||
{ |
|||
using System; |
|||
using Perspex.Threading; |
|||
|
|||
public interface IPlatformThreadingInterface |
|||
{ |
|||
Dispatcher GetThreadDispatcher(); |
|||
|
|||
void KillTimer(object timerHandle); |
|||
|
|||
object StartTimer(TimeSpan interval, Action internalTick); |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="Dispatcher.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Threading |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Security; |
|||
using System.Threading; |
|||
using Perspex.Platform; |
|||
using Splat; |
|||
|
|||
public enum DispatcherPriority |
|||
{ |
|||
Invalid = -1, |
|||
Inactive = 0, |
|||
SystemIdle = 1, |
|||
ApplicationIdle = 2, |
|||
ContextIdle = 3, |
|||
Background = 4, |
|||
Input = 5, |
|||
Loaded = 6, |
|||
Render = 7, |
|||
DataBind = 8, |
|||
Normal = 9, |
|||
Send = 10, |
|||
} |
|||
|
|||
[Flags] |
|||
internal enum Flags |
|||
{ |
|||
ShutdownStarted = 1, |
|||
Shutdown = 2, |
|||
Disabled = 4 |
|||
} |
|||
|
|||
public abstract class Dispatcher |
|||
{ |
|||
private static DispatcherFrame mainExecutionFrame = new DispatcherFrame(); |
|||
|
|||
public static Dispatcher CurrentDispatcher |
|||
{ |
|||
get { return Locator.Current.GetService<IPlatformThreadingInterface>().GetThreadDispatcher(); } |
|||
} |
|||
|
|||
public abstract bool HasShutdownFinished |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
public abstract DispatcherFrame CurrentFrame |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public static void PushFrame(DispatcherFrame frame) |
|||
{ |
|||
if (frame == null) |
|||
{ |
|||
throw new ArgumentNullException("frame"); |
|||
} |
|||
|
|||
Dispatcher dis = CurrentDispatcher; |
|||
|
|||
if (dis.HasShutdownFinished) |
|||
{ |
|||
throw new InvalidOperationException("The Dispatcher has shut down"); |
|||
} |
|||
|
|||
if (frame.Running != null) |
|||
{ |
|||
throw new InvalidOperationException("Frame is already running on a different dispatcher"); |
|||
} |
|||
|
|||
frame.ParentFrame = dis.CurrentFrame; |
|||
dis.CurrentFrame = frame; |
|||
|
|||
frame.Running = dis; |
|||
|
|||
dis.RunFrame(frame); |
|||
} |
|||
|
|||
|
|||
public static void Run() |
|||
{ |
|||
PushFrame(mainExecutionFrame); |
|||
} |
|||
|
|||
public abstract DispatcherOperation BeginInvoke(Action method); |
|||
|
|||
public abstract DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method); |
|||
|
|||
protected internal abstract void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority); |
|||
|
|||
protected abstract void RunFrame(DispatcherFrame frame); |
|||
} |
|||
} |
|||
@ -0,0 +1,140 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="DispatcherTimer.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Threading |
|||
{ |
|||
using System; |
|||
using Perspex.Platform; |
|||
using Splat; |
|||
|
|||
public class DispatcherTimer |
|||
{ |
|||
private object timerHandle; |
|||
|
|||
private DispatcherPriority priority; |
|||
|
|||
private TimeSpan interval; |
|||
|
|||
public DispatcherTimer() |
|||
{ |
|||
this.priority = DispatcherPriority.Normal; |
|||
this.Dispatcher = Dispatcher.CurrentDispatcher; |
|||
} |
|||
|
|||
public DispatcherTimer(DispatcherPriority priority) |
|||
{ |
|||
this.priority = priority; |
|||
this.Dispatcher = Dispatcher.CurrentDispatcher; |
|||
} |
|||
|
|||
public DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher) |
|||
{ |
|||
this.priority = priority; |
|||
this.Dispatcher = dispatcher; |
|||
} |
|||
|
|||
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback, Dispatcher dispatcher) |
|||
{ |
|||
this.priority = priority; |
|||
this.Dispatcher = dispatcher; |
|||
this.Interval = interval; |
|||
this.Tick += callback; |
|||
} |
|||
|
|||
~DispatcherTimer() |
|||
{ |
|||
if (this.timerHandle != null) |
|||
{ |
|||
this.Stop(); |
|||
} |
|||
} |
|||
|
|||
public event EventHandler Tick; |
|||
|
|||
public Dispatcher Dispatcher |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public TimeSpan Interval |
|||
{ |
|||
get |
|||
{ |
|||
return this.interval; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
bool enabled = this.IsEnabled; |
|||
this.Stop(); |
|||
this.interval = value; |
|||
this.IsEnabled = enabled; |
|||
} |
|||
} |
|||
|
|||
public bool IsEnabled |
|||
{ |
|||
get |
|||
{ |
|||
return this.timerHandle != null; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (this.IsEnabled != value) |
|||
{ |
|||
if (value) |
|||
{ |
|||
this.Start(); |
|||
} |
|||
else |
|||
{ |
|||
this.Stop(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public object Tag |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public void Start() |
|||
{ |
|||
if (!this.IsEnabled) |
|||
{ |
|||
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>(); |
|||
this.timerHandle = threading.StartTimer(this.Interval, this.InternalTick); |
|||
} |
|||
} |
|||
|
|||
public void Stop() |
|||
{ |
|||
if (this.IsEnabled) |
|||
{ |
|||
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>(); |
|||
threading.KillTimer(this.timerHandle); |
|||
this.timerHandle = null; |
|||
} |
|||
} |
|||
|
|||
private void InternalTick() |
|||
{ |
|||
this.Dispatcher.BeginInvoke(this.priority, (Action)this.RaiseTick); |
|||
} |
|||
|
|||
private void RaiseTick() |
|||
{ |
|||
if (this.Tick != null) |
|||
{ |
|||
this.Tick(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue