Xamarin Public Jenkins (auto-signing) e79aa3c0ed Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2016-08-03 10:59:49 +00:00

590 lines
16 KiB
C#

// TODO:
// DispatcherObject returned by BeginInvoke must allow:
// * Waiting until delegate is invoked.
// See: BeginInvoke documentation for details
//
// Implement the "Invoke" methods, they are currently not working.
//
// Add support for disabling the dispatcher and resuming it.
// Add support for Waiting for new tasks to be pushed, so that we dont busy loop.
// Add support for aborting an operation (emit the hook.operationaborted too)
//
// Very confusing information about Shutdown: it states that shutdown is
// not over, until all events are unwinded, and also states that all events
// are aborted at that point. See 'Dispatcher.InvokeShutdown' docs,
//
// Testing reveals that
// -> InvokeShutdown() stops processing, even if events are available,
// there is no "unwinding" of events, even of higher priority events,
// they are just ignored.
//
// The documentation for the Dispatcher family is poorly written, complete
// sections are cut-and-pasted that add no value and the important pieces
// like (what is a frame) is not on the APIs, but scattered everywhere else
//
// -----------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
// Copyright (c) 2016 Quamotion (http://quamotion.mobi)
//
// Authors:
// Miguel de Icaza (miguel@novell.com)
// Frederik Carlier (frederik.carlier@quamotion.mobi)
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security;
using System.Threading;
namespace System.Windows.Threading {
[Flags]
internal enum Flags {
ShutdownStarted = 1,
Shutdown = 2,
Disabled = 4
}
public sealed class Dispatcher {
static Dictionary<Thread, Dispatcher> dispatchers = new Dictionary<Thread, Dispatcher> ();
static object olock = new object ();
static DispatcherFrame main_execution_frame = new DispatcherFrame ();
const int TOP_PRIO = (int)DispatcherPriority.Send;
Thread base_thread;
PokableQueue [] priority_queues = new PokableQueue [TOP_PRIO+1];
Flags flags;
int queue_bits;
//
// Used to notify the dispatcher thread that new data is available
//
EventWaitHandle wait;
//
// The hooks for this Dispatcher
//
DispatcherHooks hooks;
//
// The current DispatcherFrame active in a given Dispatcher, we use this to
// keep a linked list of all active frames, so we can "ExitAll" frames when
// requested
DispatcherFrame current_frame;
Dispatcher (Thread t)
{
base_thread = t;
for (int i = 1; i <= (int) DispatcherPriority.Send; i++)
priority_queues [i] = new PokableQueue ();
wait = new EventWaitHandle (false, EventResetMode.AutoReset);
hooks = new DispatcherHooks (this);
}
[EditorBrowsable (EditorBrowsableState.Never)]
public bool CheckAccess ()
{
return Thread.CurrentThread == base_thread;
}
[EditorBrowsable (EditorBrowsableState.Never)]
public void VerifyAccess ()
{
if (Thread.CurrentThread != base_thread)
throw new InvalidOperationException ("Invoked from a different thread");
}
public static void ValidatePriority (DispatcherPriority priority, string parameterName)
{
if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send)
throw new InvalidEnumArgumentException (parameterName);
}
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, 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);
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);
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);
Queue (priority, op);
return op;
}
public DispatcherOperation InvokeAsync (Action callback)
{
return this.BeginInvoke(callback);
}
public DispatcherOperation InvokeAsync (Action callback, DispatcherPriority priority)
{
return this.BeginInvoke(callback, priority);
}
public DispatcherOperation InvokeAsync (Action callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
return this.BeginInvoke(callback, priority);
}
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);
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");
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");
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 ();
}
void Queue (DispatcherPriority priority, DispatcherOperation x)
{
int p = ((int) priority);
PokableQueue q = priority_queues [p];
lock (q){
int flag = 1 << p;
q.Enqueue (x);
queue_bits |= flag;
}
hooks.EmitOperationPosted (x);
if (Thread.CurrentThread != base_thread)
wait.Set ();
}
internal void Reprioritize (DispatcherOperation op, DispatcherPriority oldpriority)
{
int oldp = (int) oldpriority;
PokableQueue q = priority_queues [oldp];
lock (q){
q.Remove (op);
}
Queue (op.Priority, op);
hooks.EmitOperationPriorityChanged (op);
}
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 static Dispatcher FromThread (Thread thread)
{
Dispatcher dis;
if (dispatchers.TryGetValue (thread, out dis))
return dis;
return null;
}
public Thread Thread {
get {
return base_thread;
}
}
[SecurityCritical]
public static void Run ()
{
// Set Continue, because the previous run could clean
// this flag by Dispatcher.ExitAllFrames.
main_execution_frame.Continue = true;
PushFrame (main_execution_frame);
}
[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.dispatcher != 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.current_frame;
dis.current_frame = frame;
frame.dispatcher = dis;
dis.RunFrame (frame);
frame.dispatcher = null;
dis.current_frame = frame.ParentFrame;
frame.ParentFrame = null;
}
void PerformShutdown ()
{
EventHandler h;
h = ShutdownStarted;
if (h != null)
h (this, new EventArgs ());
flags |= Flags.Shutdown;
h = ShutdownFinished;
if (h != null)
h (this, new EventArgs ());
priority_queues = null;
wait = null;
}
void RunFrame (DispatcherFrame frame)
{
while (frame.Continue) {
while (queue_bits != 0){
for (int i = TOP_PRIO; i > 0 && queue_bits != 0; i--){
int current_bit = queue_bits & (1 << i);
if (current_bit != 0){
PokableQueue q = priority_queues [i];
do {
DispatcherOperation task;
lock (q){
// if we are done with this queue, leave.
if (q.Count == 0){
queue_bits &= ~current_bit;
break;
}
task = (DispatcherOperation) q.Dequeue ();
}
task.Invoke ();
//
// call hooks.
//
if (task.Status == DispatcherOperationStatus.Aborted)
hooks.EmitOperationAborted (task);
else
hooks.EmitOperationCompleted (task);
if (!frame.Continue)
return;
if (HasShutdownStarted){
PerformShutdown ();
return;
}
//
// If a higher-priority task comes in, go do that
//
if (current_bit < (queue_bits & ~current_bit))
{
i = TOP_PRIO + 1; // for-loop decreases by one
break;
}
} while (true);
}
}
}
hooks.EmitInactive ();
wait.WaitOne ();
wait.Reset ();
}
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
public DispatcherHooks Hooks {
[SecurityCritical]
get { throw new NotImplementedException (); }
}
public bool HasShutdownStarted {
get {
return (flags & Flags.ShutdownStarted) != 0;
}
}
public bool HasShutdownFinished {
get {
return (flags & Flags.Shutdown) != 0;
}
}
//
// Do no work here, so that any events are thrown on the owning thread
//
[SecurityCritical]
public void InvokeShutdown ()
{
flags |= Flags.ShutdownStarted;
}
[SecurityCritical]
public void BeginInvokeShutdown (DispatcherPriority priority)
{
throw new NotImplementedException ();
}
[SecurityCritical]
public static void ExitAllFrames ()
{
Dispatcher dis = CurrentDispatcher;
for (DispatcherFrame frame = dis.current_frame; frame != null; frame = frame.ParentFrame){
if (frame.exit_on_request)
frame.Continue = false;
else {
//
// Stop unwinding the frames at the first frame that is
// long running
break;
}
}
}
public DispatcherProcessingDisabled DisableProcessing ()
{
throw new NotImplementedException ();
}
public event EventHandler ShutdownStarted;
public event EventHandler ShutdownFinished;
public event DispatcherUnhandledExceptionEventHandler UnhandledException;
public event DispatcherUnhandledExceptionFilterEventHandler UnhandledExceptionFilter;
}
internal class PokableQueue {
const int initial_capacity = 32;
int size, head, tail;
object [] array;
internal PokableQueue (int capacity)
{
array = new object [capacity];
}
internal PokableQueue () : this (initial_capacity)
{
}
public void Enqueue (object obj)
{
if (size == array.Length)
Grow ();
array[tail] = obj;
tail = (tail+1) % array.Length;
size++;
}
public object Dequeue ()
{
if (size < 1)
throw new InvalidOperationException ();
object result = array[head];
array [head] = null;
head = (head + 1) % array.Length;
size--;
return result;
}
void Grow ()
{
int newc = array.Length * 2;
object[] new_contents = new object[newc];
array.CopyTo (new_contents, 0);
array = new_contents;
head = 0;
tail = head + size;
}
public int Count {
get {
return size;
}
}
public void Remove (object obj)
{
for (int i = 0; i < size; i++){
if (array [(head+i) % array.Length] == obj){
for (int j = i; j < size-i; j++)
array [(head +j) % array.Length] = array [(head+j+1) % array.Length];
size--;
if (size < 0)
size = array.Length-1;
tail--;
}
}
}
}
}